diff mbox series

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

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

Commit Message

Alex Coplan June 28, 2023, 10:35 a.m. UTC
Hi,

This patch implements clang's __has_feature and __has_extension in GCC.
This is a v2 of the original RFC posted here:

https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html

Changes since v1:
 - Follow the clang behaviour where -pedantic-errors means that
   __has_extension behaves exactly like __has_feature.
 - We're now more conservative with reporting C++ features as extensions
   available in C++98. For features where we issue a pedwarn in C++98
   mode, we no longer report these as available extensions for C++98.
 - Switch to using a hash_map to store the features. As well as ensuring
   lookup is constant time, this allows us to dynamically register
   features (right now based on frontend, but later we could allow the
   target to register additional features).
 - Also implement some Objective-C features, add a langhook to dispatch
   to each frontend to allow it to register language-specific features.

There is an outstanding question around what to do with
cxx_binary_literals in the C frontend for C2x. Should we introduce a new
c_binary_literals feature that is a feature in C2x and an extension
below that, or should we just continue using the cxx_binary_literals
feature and mark that as a standard feature in C2x? See the comment in
c_feature_table in the patch.

There is also some doubt over what to do with the undocumented "tls"
feature.  In clang this is gated on whether the target supports TLS, but
in clang (unlike GCC) it is a hard error to use TLS when the target
doesn't support it.  In GCC I believe you can always use TLS, you just
get emulated TLS in the case that the target doesn't support it
natively.  So in this patch GCC always reports having the "tls" feature.
Would appreciate if anyone has feedback on this aspect.

I know Iain was concerned that it should be possible to have
target-specific features. Hopefully it is clear that the design in this
patch is more amenable in this. I think for Darwin it should be possible
to add a targetcm hook to register additional features (either passing
through a callback to allow the target code to add to the hash_map, or
exposing a separate langhook that the target can call to register
features).

Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin. Any
thoughts?

Thanks,
Alex

------

Co-Authored-By: Iain Sandoe <iain@sandoe.co.uk>

gcc/c-family/ChangeLog:

	PR c++/60512
	* c-common.cc (struct hf_feature_info): New.
	(struct hf_table_entry): New.
	(hf_generic_predicate): New.
	(c_common_register_feature): New.
	(init_has_feature): New.
	(has_feature_p): New.
	* c-common.h (c_common_has_feature): New.
	(has_feature_p): New.
	(c_common_register_feature): 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 (LANG_HOOKS_REGISTER_FEATURES): Implement with
	c_register_features.
	* c-objc-common.cc (struct c_feature_info): New.
	(c_has_feature): New.
	(c_register_features): New.

gcc/cp/ChangeLog:

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

gcc/ChangeLog:

	PR c++/60512
	* doc/cpp.texi: Document __has_{feature,extension}.
	* langhooks-def.h (LANG_HOOKS_REGISTER_FEATURES): New.
	(LANG_HOOKS_INITIALIZER): Add LANG_HOOKS_REGISTER_FEATURES.
	* langhooks.h (struct lang_hooks): Add register_features hook.

gcc/objc/ChangeLog:

	PR c++/60512
	* objc-act.cc (struct objc_feature_info): New.
	(objc_nonfragile_abi_p): New.
	(objc_has_feature): New.
	(objc_common_register_features): New.
	* objc-act.h (objc_register_features): New.
	(objc_common_register_features): New.
	* objc-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with
	objc_register_features.
	(objc_register_features): New.

gcc/objcp/ChangeLog:

	PR c++/60512
	* objcp-lang.cc (objcxx_register_features): New.
	(LANG_HOOKS_REGISTER_FEATURES): Implement with
	objcxx_register_features.

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 July 26, 2023, 2 p.m. UTC | #1
On 28/06/2023 11:35, Alex Coplan via Gcc-patches wrote:
> Hi,
> 
> This patch implements clang's __has_feature and __has_extension in GCC.
> This is a v2 of the original RFC posted here:

Ping. The Objective-C parts have been approved, but the C, C++, and generic bits
need review.

Let me know if there's anything I can do to make it easier to review, e.g. would
it help to split into a series which adds the language-specific bits in separate
patches?

Thanks,
Alex

> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html
> 
> Changes since v1:
>  - Follow the clang behaviour where -pedantic-errors means that
>    __has_extension behaves exactly like __has_feature.
>  - We're now more conservative with reporting C++ features as extensions
>    available in C++98. For features where we issue a pedwarn in C++98
>    mode, we no longer report these as available extensions for C++98.
>  - Switch to using a hash_map to store the features. As well as ensuring
>    lookup is constant time, this allows us to dynamically register
>    features (right now based on frontend, but later we could allow the
>    target to register additional features).
>  - Also implement some Objective-C features, add a langhook to dispatch
>    to each frontend to allow it to register language-specific features.
> 
> There is an outstanding question around what to do with
> cxx_binary_literals in the C frontend for C2x. Should we introduce a new
> c_binary_literals feature that is a feature in C2x and an extension
> below that, or should we just continue using the cxx_binary_literals
> feature and mark that as a standard feature in C2x? See the comment in
> c_feature_table in the patch.
> 
> There is also some doubt over what to do with the undocumented "tls"
> feature.  In clang this is gated on whether the target supports TLS, but
> in clang (unlike GCC) it is a hard error to use TLS when the target
> doesn't support it.  In GCC I believe you can always use TLS, you just
> get emulated TLS in the case that the target doesn't support it
> natively.  So in this patch GCC always reports having the "tls" feature.
> Would appreciate if anyone has feedback on this aspect.
> 
> I know Iain was concerned that it should be possible to have
> target-specific features. Hopefully it is clear that the design in this
> patch is more amenable in this. I think for Darwin it should be possible
> to add a targetcm hook to register additional features (either passing
> through a callback to allow the target code to add to the hash_map, or
> exposing a separate langhook that the target can call to register
> features).
> 
> Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin. Any
> thoughts?
> 
> Thanks,
> Alex
> 
> ------
> 
> Co-Authored-By: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/c-family/ChangeLog:
> 
> 	PR c++/60512
> 	* c-common.cc (struct hf_feature_info): New.
> 	(struct hf_table_entry): New.
> 	(hf_generic_predicate): New.
> 	(c_common_register_feature): New.
> 	(init_has_feature): New.
> 	(has_feature_p): New.
> 	* c-common.h (c_common_has_feature): New.
> 	(has_feature_p): New.
> 	(c_common_register_feature): 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 (LANG_HOOKS_REGISTER_FEATURES): Implement with
> 	c_register_features.
> 	* c-objc-common.cc (struct c_feature_info): New.
> 	(c_has_feature): New.
> 	(c_register_features): New.
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/60512
> 	* cp-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with
> 	cp_register_features.
> 	* cp-objcp-common.cc (struct cp_feature_selector): New.
> 	(cp_feature_selector::has_feature): New.
> 	(struct cp_feature_info): New.
> 	(cp_has_feature): New.
> 	(cp_register_features): New.
> 
> gcc/ChangeLog:
> 
> 	PR c++/60512
> 	* doc/cpp.texi: Document __has_{feature,extension}.
> 	* langhooks-def.h (LANG_HOOKS_REGISTER_FEATURES): New.
> 	(LANG_HOOKS_INITIALIZER): Add LANG_HOOKS_REGISTER_FEATURES.
> 	* langhooks.h (struct lang_hooks): Add register_features hook.
> 
> gcc/objc/ChangeLog:
> 
> 	PR c++/60512
> 	* objc-act.cc (struct objc_feature_info): New.
> 	(objc_nonfragile_abi_p): New.
> 	(objc_has_feature): New.
> 	(objc_common_register_features): New.
> 	* objc-act.h (objc_register_features): New.
> 	(objc_common_register_features): New.
> 	* objc-lang.cc (LANG_HOOKS_REGISTER_FEATURES): Implement with
> 	objc_register_features.
> 	(objc_register_features): New.
> 
> gcc/objcp/ChangeLog:
> 
> 	PR c++/60512
> 	* objcp-lang.cc (objcxx_register_features): New.
> 	(LANG_HOOKS_REGISTER_FEATURES): Implement with
> 	objcxx_register_features.
> 
> 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.
> 

> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index 34566a342bd..955713e9592 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -311,6 +311,34 @@ const struct fname_var_t fname_vars[] =
>    {NULL, 0, 0},
>  };
>  
> +enum
> +{
> +  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
> +  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
> +};
> +
> +struct hf_feature_info
> +{
> +  const char *ident;
> +  unsigned flags;
> +  unsigned mask;
> +};
> +
> +static const hf_feature_info has_feature_table[] =
> +{
> +  { "address_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_ADDRESS },
> +  { "thread_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_THREAD },
> +  { "leak_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_LEAK },
> +  { "hwaddress_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_HWADDRESS },
> +  { "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED },
> +  { "attribute_deprecated_with_message",  0, 0 },
> +  { "attribute_unavailable_with_message", 0, 0 },
> +  { "enumerator_attributes",		  0, 0 },
> +  { "tls", 0, 0 },
> +  { "gnu_asm_goto_with_outputs",	  HF_FLAG_EXT, 0 },
> +  { "gnu_asm_goto_with_outputs_full",	  HF_FLAG_EXT, 0 }
> +};
> +
>  /* Global visibility options.  */
>  struct visibility_flags visibility_options;
>  
> @@ -9549,4 +9577,66 @@ c_strict_flex_array_level_of (tree array_field)
>    return strict_flex_array_level;
>  }
>  
> +struct hf_table_entry
> +{
> +  hf_predicate predicate;
> +  const void *info;
> +};
> +
> +static bool hf_generic_predicate (bool strict_p, const void *param)
> +{
> +  auto info = static_cast <const hf_feature_info *>(param);
> +  if ((info->flags & HF_FLAG_EXT) && strict_p)
> +    return false;
> +
> +  if (info->flags & HF_FLAG_SANITIZE)
> +    return flag_sanitize & info->mask;
> +
> +  return true;
> +}
> +
> +typedef hash_map <tree, hf_table_entry> feature_map_t;
> +feature_map_t *feature_map;
> +
> +void
> +c_common_register_feature (const char *name,
> +			   hf_predicate pred,
> +			   const void *info)
> +{
> +  hf_table_entry e { pred, info };
> +  bool dup = feature_map->put (get_identifier (name), e);
> +  gcc_checking_assert (!dup);
> +}
> +
> +static void
> +init_has_feature ()
> +{
> +  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;
> +      c_common_register_feature (info->ident,
> +				 hf_generic_predicate,
> +				 static_cast <const void *>(info));
> +    }
> +
> +  /* Register language-specific features.  */
> +  lang_hooks.register_features ();
> +}
> +
> +bool
> +has_feature_p (const char *feat, bool strict_p)
> +{
> +  if (!feature_map)
> +    init_has_feature ();
> +
> +  const hf_table_entry *e = feature_map->get (get_identifier (feat));
> +  if (!e)
> +    return false;
> +
> +  return e->predicate (strict_p, e->info);
> +}
> +
>  #include "gt-c-family-c-common.h"
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index b5ef5ff6b2c..5122767a8a5 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -1123,6 +1123,15 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level,
>       ATTRIBUTE_GCC_DIAG(5,0);
>  extern int c_common_has_attribute (cpp_reader *, bool);
>  extern int c_common_has_builtin (cpp_reader *);
> +extern int c_common_has_feature (cpp_reader *, bool);
> +extern bool has_feature_p (const char *, bool);
> +
> +typedef bool (*hf_predicate) (bool, const void *);
> +extern void c_common_register_feature (const char *,
> +				       hf_predicate,
> +				       const void *);
> +extern void c_register_features ();
> +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 dcd061c7cb1..ddc8b349dbe 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;
> @@ -425,16 +426,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;
>      }
>  
> @@ -454,7 +455,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;
>      }
> @@ -473,9 +474,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..e1b8bde31d7 100644
> --- a/gcc/c/c-lang.cc
> +++ b/gcc/c/c-lang.cc
> @@ -49,6 +49,9 @@ enum c_language_kind c_language = clk_c;
>  #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
>  #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language
>  
> +#undef LANG_HOOKS_REGISTER_FEATURES
> +#define LANG_HOOKS_REGISTER_FEATURES c_register_features
> +
>  /* Each front end provides its own lang hook initializer.  */
>  struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
>  
> diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
> index e4aed61ed00..1ab28f0ce12 100644
> --- a/gcc/c/c-objc-common.cc
> +++ b/gcc/c/c-objc-common.cc
> @@ -34,6 +34,49 @@ along with GCC; see the file COPYING3.  If not see
>  static bool c_tree_printer (pretty_printer *, text_info *, const char *,
>  			    int, bool, bool, bool, bool *, const char **);
>  
> +struct c_feature_info
> +{
> +  const char *ident;
> +  const int *enable_flag;
> +};
> +
> +static const c_feature_info c_feature_table[] =
> +{
> +  { "c_alignas", &flag_isoc11 },
> +  { "c_alignof", &flag_isoc11 },
> +  { "c_atomic", &flag_isoc11 },
> +  { "c_generic_selections", &flag_isoc11 },
> +  { "c_static_assert", &flag_isoc11 },
> +  { "c_thread_local", &flag_isoc11 },
> +
> +  /* XXX: Binary literals are available as a standard feature in
> +     C2x.  They are standardised in C++14 and available as an extension
> +     in all C versions.  Would it make more sense to have
> +     cxx_binary_literals always report as an extension (in C) and add a
> +     new c_binary_literals that reports as a feature for -std=c2x and an
> +     extension below that?  */
> +  { "cxx_binary_literals", &flag_isoc2x }
> +};
> +
> +static bool
> +c_has_feature (bool strict_p, const void *arg)
> +{
> +  auto info = static_cast <const c_feature_info *>(arg);
> +  return !info->enable_flag || !strict_p || *info->enable_flag;
> +}
> +
> +void
> +c_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
> +    {
> +      const c_feature_info *info = c_feature_table + i;
> +      c_common_register_feature (info->ident,
> +				 c_has_feature,
> +				 static_cast <const void *>(info));
> +    }
> +}
> +
>  bool
>  c_missing_noreturn_ok_p (tree decl)
>  {
> diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
> index 2f541460c4a..e3d65c8c8d2 100644
> --- a/gcc/cp/cp-lang.cc
> +++ b/gcc/cp/cp-lang.cc
> @@ -104,6 +104,9 @@ static const char *cp_get_sarif_source_language (const char *);
>  #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
>  #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE cp_get_sarif_source_language
>  
> +#undef LANG_HOOKS_REGISTER_FEATURES
> +#define LANG_HOOKS_REGISTER_FEATURES cp_register_features
> +
>  /* Each front end provides its own lang hook initializer.  */
>  struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
>  
> diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
> index 93b027b80ce..18f2a7d0fdc 100644
> --- a/gcc/cp/cp-objcp-common.cc
> +++ b/gcc/cp/cp-objcp-common.cc
> @@ -23,10 +23,131 @@ along with GCC; see the file COPYING3.  If not see
>  #include "coretypes.h"
>  #include "cp-tree.h"
>  #include "cp-objcp-common.h"
> +#include "c-family/c-common.h"
>  #include "dwarf2.h"
>  #include "stringpool.h"
>  #include "contracts.h"
>  
> +struct cp_feature_selector
> +{
> +  enum
> +  {
> +    DIALECT,
> +    FLAG
> +  } kind;
> +
> +  union
> +  {
> +    const int *enable_flag;
> +    struct {
> +      enum cxx_dialect feat;
> +      enum cxx_dialect ext;
> +    } dialect;
> +  };
> +
> +  constexpr cp_feature_selector (const int *flag)
> +    : kind (FLAG), enable_flag (flag) {}
> +  constexpr cp_feature_selector (enum cxx_dialect feat,
> +				 enum cxx_dialect ext)
> +    : kind (DIALECT), dialect{feat, ext} {}
> +  constexpr cp_feature_selector (enum cxx_dialect feat)
> +    : cp_feature_selector (feat, feat) {}
> +
> +  bool has_feature (bool strict_p) const;
> +};
> +
> +bool cp_feature_selector::has_feature (bool strict_p) const
> +{
> +  switch (kind)
> +    {
> +    case DIALECT:
> +      if (!strict_p)
> +	return cxx_dialect >= dialect.ext;
> +      return cxx_dialect >= dialect.feat;
> +    case FLAG:
> +      return *enable_flag;
> +    }
> +  gcc_unreachable ();
> +}
> +
> +struct cp_feature_info
> +{
> +  const char *ident;
> +  cp_feature_selector selector;
> +};
> +
> +static const cp_feature_info cp_feature_table[] =
> +{
> +  { "cxx_exceptions", &flag_exceptions },
> +  { "cxx_rtti", &flag_rtti },
> +  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
> +  { "cxx_alias_templates", cxx11 },
> +  { "cxx_alignas", cxx11 },
> +  { "cxx_alignof", cxx11 },
> +  { "cxx_attributes", cxx11 },
> +  { "cxx_constexpr", cxx11 },
> +  { "cxx_constexpr_string_builtins", cxx11 },
> +  { "cxx_decltype", cxx11 },
> +  { "cxx_decltype_incomplete_return_types", cxx11 },
> +  { "cxx_default_function_template_args", cxx11 },
> +  { "cxx_defaulted_functions", cxx11 },
> +  { "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 },
> +};
> +
> +static bool
> +cp_has_feature (bool strict_p, const void *arg)
> +{
> +  const auto info = static_cast <const cp_feature_info *>(arg);
> +  return info->selector.has_feature (strict_p);
> +}
> +
> +void
> +cp_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
> +    {
> +      const cp_feature_info *info = cp_feature_table + i;
> +      c_common_register_feature (info->ident,
> +				 cp_has_feature,
> +				 static_cast <const void *>(info));
> +    }
> +}
> +
>  /* 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..76dbb9892d6 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,33 @@ the operator is as follows:
>  #endif
>  @end smallexample
>  
> +@node @code{__has_feature}
> +@subsection @code{__has_feature}
> +@cindex @code{__has_feature}
> +
> +The special operator @code{__has_feature (@var{operand})} may be used in
> +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
> +expressions to test whether the identifier given in @var{operand} is recognized
> +as a feature supported by GCC given the current options and, in the case of
> +standard language features, whether the feature is available in the chosen
> +version of the language standard.
> +
> +@node @code{__has_extension}
> +@subsection @code{__has_extension}
> +@cindex @code{__has_extension}
> +
> +The special operator @code{__has_extension (@var{operand})} may be used in
> +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
> +expressions to test whether the identifier given in @var{operand} is recognized
> +as an extension supported by GCC given the current options.  In any given
> +context, the features accepted by @code{__has_extension} are a strict superset
> +of those accepted by @code{__has_feature}.  Unlike @code{__has_feature},
> +@code{__has_extension} tests whether a given feature is available regardless of
> +strict language standards conformance.
> +
> +If the @code{-pedantic-errors} flag is given, @code{__has_extension} is
> +equivalent to @code{__has_feature}.
> +
>  @node @code{__has_include}
>  @subsection @code{__has_include}
>  @cindex @code{__has_include}
> diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
> index c6d18526360..46f9af02afa 100644
> --- a/gcc/langhooks-def.h
> +++ b/gcc/langhooks-def.h
> @@ -151,6 +151,7 @@ extern const char *lhd_get_sarif_source_language (const char *);
>  #define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location
>  #define LANG_HOOKS_FINALIZE_EARLY_DEBUG lhd_finalize_early_debug
>  #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language
> +#define LANG_HOOKS_REGISTER_FEATURES lhd_do_nothing
>  
>  /* Attribute hooks.  */
>  #define LANG_HOOKS_ATTRIBUTE_TABLE		NULL
> @@ -394,7 +395,8 @@ extern void lhd_end_section (void);
>    LANG_HOOKS_RUN_LANG_SELFTESTS, \
>    LANG_HOOKS_GET_SUBSTRING_LOCATION, \
>    LANG_HOOKS_FINALIZE_EARLY_DEBUG,   \
> -  LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE \
> +  LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE, \
> +  LANG_HOOKS_REGISTER_FEATURES \
>  }
>  
>  #endif /* GCC_LANG_HOOKS_DEF_H */
> diff --git a/gcc/langhooks.h b/gcc/langhooks.h
> index cca75285fc2..009a03c0db6 100644
> --- a/gcc/langhooks.h
> +++ b/gcc/langhooks.h
> @@ -643,6 +643,8 @@ struct lang_hooks
>       languages.  */
>    const char *(*get_sarif_source_language) (const char *filename);
>  
> +  void (*register_features) ();
> +
>    /* Whenever you add entries here, make sure you adjust langhooks-def.h
>       and langhooks.cc accordingly.  */
>  };
> diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
> index e4c49e664e1..e80dde07728 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)
>      }
>  }
>  
> +typedef bool (*objc_feature_p)();
> +
> +struct objc_feature_info
> +{
> +  const char *ident;
> +  objc_feature_p predicate;
> +
> +  objc_feature_info (const char *name) : ident (name) {}
> +  objc_feature_info (const char *name, objc_feature_p p)
> +    : ident (name), predicate (p) {}
> +
> +  bool has_feature (bool) const
> +    {
> +      return predicate ? predicate () : true;
> +    }
> +};
> +
> +static bool objc_nonfragile_abi_p ()
> +{
> +  return flag_next_runtime && flag_objc_abi >= 2;
> +}
> +
> +static bool objc_has_feature (bool strict_p, const void *arg)
> +{
> +  auto info = static_cast <const objc_feature_info *>(arg);
> +  return info->has_feature (strict_p);
> +}
> +
> +static const objc_feature_info objc_features[] =
> +{
> +  { "objc_default_synthesize_properties" },
> +  { "objc_instancetype" },
> +  { "objc_nonfragile_abi", objc_nonfragile_abi_p }
> +};
> +
> +void
> +objc_common_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++)
> +    {
> +      const objc_feature_info *info = objc_features + i;
> +      c_common_register_feature (info->ident,
> +				 objc_has_feature,
> +				 static_cast <const void *>(info));
> +    }
> +}
>  
>  #include "gt-objc-objc-act.h"
> diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
> index e21ab52d8ca..799e289342e 100644
> --- a/gcc/objc/objc-act.h
> +++ b/gcc/objc/objc-act.h
> @@ -28,6 +28,10 @@ const char *objc_printable_name (tree, int);
>  int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
>  void objc_common_init_ts (void);
>  const char *objc_get_sarif_source_language (const char *);
> +void objc_register_features ();
> +
> +/* 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..103b5ca0673 100644
> --- a/gcc/objc/objc-lang.cc
> +++ b/gcc/objc/objc-lang.cc
> @@ -48,6 +48,8 @@ enum c_language_kind c_language = clk_objc;
>  #define LANG_HOOKS_TREE_SIZE objc_common_tree_size
>  #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
>  #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
> +#undef LANG_HOOKS_REGISTER_FEATURES
> +#define LANG_HOOKS_REGISTER_FEATURES objc_register_features
>  
>  /* Each front end provides its own lang hook initializer.  */
>  struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
> @@ -58,6 +60,12 @@ objc_get_sarif_source_language (const char *)
>    return "objectivec";
>  }
>  
> +void objc_register_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..c1bf0914a47 100644
> --- a/gcc/objcp/objcp-lang.cc
> +++ b/gcc/objcp/objcp-lang.cc
> @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
>  
>  enum c_language_kind c_language = clk_objcxx;
>  static void objcxx_init_ts (void);
> +static void objcxx_register_features ();
>  
>  /* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h;
>     consequently, there should be very few hooks below.  */
> @@ -42,6 +43,8 @@ static void objcxx_init_ts (void);
>  #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr
>  #undef LANG_HOOKS_INIT_TS
>  #define LANG_HOOKS_INIT_TS objcxx_init_ts
> +#undef LANG_HOOKS_REGISTER_FEATURES
> +#define LANG_HOOKS_REGISTER_FEATURES objcxx_register_features
>  
>  /* Each front end provides its own lang hook initializer.  */
>  struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
> @@ -87,4 +90,11 @@ objcxx_init_ts (void)
>    cp_common_init_ts ();
>  }
>  
> +static void
> +objcxx_register_features ()
> +{
> +  objc_common_register_features ();
> +  cp_register_features ();
> +}
> +
>  #include "gtype-objcp.h"
> 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 aef703f8111..3586e2e9399 100644
> --- a/libcpp/include/cpplib.h
> +++ b/libcpp/include/cpplib.h
> @@ -755,6 +755,9 @@ struct cpp_callbacks
>    /* Callback to determine whether a built-in function is recognized.  */
>    int (*has_builtin) (cpp_reader *);
>  
> +  /* Callback to determine whether a feature is available.  */
> +  int (*has_feature) (cpp_reader *, bool);
> +
>    /* Callback that can change a user lazy into normal macro.  */
>    void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned);
>  
> @@ -959,7 +962,9 @@ enum cpp_builtin_type
>    BT_HAS_STD_ATTRIBUTE,		/* `__has_c_attribute(x)' */
>    BT_HAS_BUILTIN,		/* `__has_builtin(x)' */
>    BT_HAS_INCLUDE,		/* `__has_include(x)' */
> -  BT_HAS_INCLUDE_NEXT		/* `__has_include_next(x)' */
> +  BT_HAS_INCLUDE_NEXT,		/* `__has_include_next(x)' */
> +  BT_HAS_FEATURE,		/* `__has_feature(x)' */
> +  BT_HAS_EXTENSION		/* `__has_extension(x)' */
>  };
>  
>  #define CPP_HASHNODE(HNODE)	((cpp_hashnode *) (HNODE))
> diff --git a/libcpp/init.cc b/libcpp/init.cc
> index 693feaa31ed..b7081db3907 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)
Jason Merrill July 26, 2023, 8:26 p.m. UTC | #2
On 6/28/23 06:35, Alex Coplan wrote:
> Hi,
> 
> This patch implements clang's __has_feature and __has_extension in GCC.
> This is a v2 of the original RFC posted here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html
> 
> Changes since v1:
>   - Follow the clang behaviour where -pedantic-errors means that
>     __has_extension behaves exactly like __has_feature.
>   - We're now more conservative with reporting C++ features as extensions
>     available in C++98. For features where we issue a pedwarn in C++98
>     mode, we no longer report these as available extensions for C++98.
>   - Switch to using a hash_map to store the features. As well as ensuring
>     lookup is constant time, this allows us to dynamically register
>     features (right now based on frontend, but later we could allow the
>     target to register additional features).
>   - Also implement some Objective-C features, add a langhook to dispatch
>     to each frontend to allow it to register language-specific features.

Hmm, it seems questionable to use a generic langhook for something that 
the generic code doesn't care about, only the c-family front ends.  A 
common pattern in c-family is to declare a signature in c-common.h and 
define it differently for the various front-ends, i.e. in the *-lang.cc 
files.

> There is an outstanding question around what to do with
> cxx_binary_literals in the C frontend for C2x. Should we introduce a new
> c_binary_literals feature that is a feature in C2x and an extension
> below that, or should we just continue using the cxx_binary_literals
> feature and mark that as a standard feature in C2x? See the comment in
> c_feature_table in the patch.

What does clang do here?

> There is also some doubt over what to do with the undocumented "tls"
> feature.  In clang this is gated on whether the target supports TLS, but
> in clang (unlike GCC) it is a hard error to use TLS when the target
> doesn't support it.  In GCC I believe you can always use TLS, you just
> get emulated TLS in the case that the target doesn't support it
> natively.  So in this patch GCC always reports having the "tls" feature.
> Would appreciate if anyone has feedback on this aspect.

Hmm, I don't think GCC always supports TLS, given that the testsuite has 
a predicate to check for that support (and others to check for emulated 
or native support).

But I think it's right to report having "tls" for emulated support.

> I know Iain was concerned that it should be possible to have
> target-specific features. Hopefully it is clear that the design in this
> patch is more amenable in this. I think for Darwin it should be possible
> to add a targetcm hook to register additional features (either passing
> through a callback to allow the target code to add to the hash_map, or
> exposing a separate langhook that the target can call to register
> features).

The design seems a bit complicated still, with putting a callback into 
the map.  Do we need the callbacks?  Do we expect the value of 
__has_feature to change at different points in compilation?  Does that 
happen in clang?

> Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin. Any
> thoughts?

Most of the patch needs more comments, particularly before various 
top-level definitions.

Jason
Alex Coplan Aug. 2, 2023, 10:47 a.m. UTC | #3
On 26/07/2023 16:26, Jason Merrill wrote:
> On 6/28/23 06:35, Alex Coplan wrote:
> > Hi,
> > 
> > This patch implements clang's __has_feature and __has_extension in GCC.
> > This is a v2 of the original RFC posted here:
> > 
> > https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html
> > 
> > Changes since v1:
> >   - Follow the clang behaviour where -pedantic-errors means that
> >     __has_extension behaves exactly like __has_feature.
> >   - We're now more conservative with reporting C++ features as extensions
> >     available in C++98. For features where we issue a pedwarn in C++98
> >     mode, we no longer report these as available extensions for C++98.
> >   - Switch to using a hash_map to store the features. As well as ensuring
> >     lookup is constant time, this allows us to dynamically register
> >     features (right now based on frontend, but later we could allow the
> >     target to register additional features).
> >   - Also implement some Objective-C features, add a langhook to dispatch
> >     to each frontend to allow it to register language-specific features.
> 
> Hmm, it seems questionable to use a generic langhook for something that the
> generic code doesn't care about, only the c-family front ends.  A common
> pattern in c-family is to declare a signature in c-common.h and define it
> differently for the various front-ends, i.e. in the *-lang.cc files.

Thanks. I wasn't sure if, for each frontend, there was a source file
that gets linked into exactly one frontend, but it looks like the
*-lang.cc files will do the job. I'll rework the patch to drop the
langhook and use this approach instead.

> 
> > There is an outstanding question around what to do with
> > cxx_binary_literals in the C frontend for C2x. Should we introduce a new
> > c_binary_literals feature that is a feature in C2x and an extension
> > below that, or should we just continue using the cxx_binary_literals
> > feature and mark that as a standard feature in C2x? See the comment in
> > c_feature_table in the patch.
> 
> What does clang do here?

The status quo in clang is that there is no identifier that gets
reported as a feature for this in C (even with -std=c2x).
cxx_binary_literals is reported just as an extension (even with -std=c2x).
It does seem that there should be at least one identifier which reports
this as a feature with -std=c2x, though. WDYT?

> 
> > There is also some doubt over what to do with the undocumented "tls"
> > feature.  In clang this is gated on whether the target supports TLS, but
> > in clang (unlike GCC) it is a hard error to use TLS when the target
> > doesn't support it.  In GCC I believe you can always use TLS, you just
> > get emulated TLS in the case that the target doesn't support it
> > natively.  So in this patch GCC always reports having the "tls" feature.
> > Would appreciate if anyone has feedback on this aspect.
> 
> Hmm, I don't think GCC always supports TLS, given that the testsuite has a
> predicate to check for that support (and others to check for emulated or
> native support).

Hmm, I see there is a check_effective_target_tls predicate for this,
indeed. I wonder if this might be a holdover, though. I can't seem to
configure a GCC without TLS. Even if I configure with
--target=aarch64-none-elf --disable-tls, for example, I get emutls
if I compile code using thread-local variables.

Do we know of a GCC configuration where thread-local variables actually
get rejected (and hence check_effective_target_tls returns false)?

> 
> But I think it's right to report having "tls" for emulated support.
> 
> > I know Iain was concerned that it should be possible to have
> > target-specific features. Hopefully it is clear that the design in this
> > patch is more amenable in this. I think for Darwin it should be possible
> > to add a targetcm hook to register additional features (either passing
> > through a callback to allow the target code to add to the hash_map, or
> > exposing a separate langhook that the target can call to register
> > features).
> 
> The design seems a bit complicated still, with putting a callback into the
> map.  Do we need the callbacks?  Do we expect the value of __has_feature to
> change at different points in compilation?  Does that happen in clang?

This is a good point. Certainly if we were to add features that depend
on the target architecture features, then this can change mid-way
through a TU, so having this flexibility in the design does provide some
potential future-proofing.

I had a look through the existing features, and I did wonder about cases
like this:

__attribute__((no_sanitize("undefined")))
int f() {
  return __has_feature (undefined_behavior_sanitizer);
}

but of course since __has_feature is evaluated during preprocessing,
there's no way that the attribute could be taken into account here (and
indeed clang does not).

I'll drop the callbacks from the patch for now, unless you think we
should keep them for future-proofing.

> 
> > Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin. Any
> > thoughts?
> 
> Most of the patch needs more comments, particularly before various top-level
> definitions.

Ack. I'll add more comments in the next revision.

Thanks a lot for the review.

Alex

> 
> Jason
>
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 34566a342bd..955713e9592 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -311,6 +311,34 @@  const struct fname_var_t fname_vars[] =
   {NULL, 0, 0},
 };
 
+enum
+{
+  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
+  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
+};
+
+struct hf_feature_info
+{
+  const char *ident;
+  unsigned flags;
+  unsigned mask;
+};
+
+static const hf_feature_info has_feature_table[] =
+{
+  { "address_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_ADDRESS },
+  { "thread_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_THREAD },
+  { "leak_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_LEAK },
+  { "hwaddress_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_HWADDRESS },
+  { "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED },
+  { "attribute_deprecated_with_message",  0, 0 },
+  { "attribute_unavailable_with_message", 0, 0 },
+  { "enumerator_attributes",		  0, 0 },
+  { "tls", 0, 0 },
+  { "gnu_asm_goto_with_outputs",	  HF_FLAG_EXT, 0 },
+  { "gnu_asm_goto_with_outputs_full",	  HF_FLAG_EXT, 0 }
+};
+
 /* Global visibility options.  */
 struct visibility_flags visibility_options;
 
@@ -9549,4 +9577,66 @@  c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+struct hf_table_entry
+{
+  hf_predicate predicate;
+  const void *info;
+};
+
+static bool hf_generic_predicate (bool strict_p, const void *param)
+{
+  auto info = static_cast <const hf_feature_info *>(param);
+  if ((info->flags & HF_FLAG_EXT) && strict_p)
+    return false;
+
+  if (info->flags & HF_FLAG_SANITIZE)
+    return flag_sanitize & info->mask;
+
+  return true;
+}
+
+typedef hash_map <tree, hf_table_entry> feature_map_t;
+feature_map_t *feature_map;
+
+void
+c_common_register_feature (const char *name,
+			   hf_predicate pred,
+			   const void *info)
+{
+  hf_table_entry e { pred, info };
+  bool dup = feature_map->put (get_identifier (name), e);
+  gcc_checking_assert (!dup);
+}
+
+static void
+init_has_feature ()
+{
+  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;
+      c_common_register_feature (info->ident,
+				 hf_generic_predicate,
+				 static_cast <const void *>(info));
+    }
+
+  /* Register language-specific features.  */
+  lang_hooks.register_features ();
+}
+
+bool
+has_feature_p (const char *feat, bool strict_p)
+{
+  if (!feature_map)
+    init_has_feature ();
+
+  const hf_table_entry *e = feature_map->get (get_identifier (feat));
+  if (!e)
+    return false;
+
+  return e->predicate (strict_p, e->info);
+}
+
 #include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b5ef5ff6b2c..5122767a8a5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1123,6 +1123,15 @@  extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level,
      ATTRIBUTE_GCC_DIAG(5,0);
 extern int c_common_has_attribute (cpp_reader *, bool);
 extern int c_common_has_builtin (cpp_reader *);
+extern int c_common_has_feature (cpp_reader *, bool);
+extern bool has_feature_p (const char *, bool);
+
+typedef bool (*hf_predicate) (bool, const void *);
+extern void c_common_register_feature (const char *,
+				       hf_predicate,
+				       const void *);
+extern void c_register_features ();
+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 dcd061c7cb1..ddc8b349dbe 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;
@@ -425,16 +426,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;
     }
 
@@ -454,7 +455,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;
     }
@@ -473,9 +474,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..e1b8bde31d7 100644
--- a/gcc/c/c-lang.cc
+++ b/gcc/c/c-lang.cc
@@ -49,6 +49,9 @@  enum c_language_kind c_language = clk_c;
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language
 
+#undef LANG_HOOKS_REGISTER_FEATURES
+#define LANG_HOOKS_REGISTER_FEATURES c_register_features
+
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index e4aed61ed00..1ab28f0ce12 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -34,6 +34,49 @@  along with GCC; see the file COPYING3.  If not see
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
 			    int, bool, bool, bool, bool *, const char **);
 
+struct c_feature_info
+{
+  const char *ident;
+  const int *enable_flag;
+};
+
+static const c_feature_info c_feature_table[] =
+{
+  { "c_alignas", &flag_isoc11 },
+  { "c_alignof", &flag_isoc11 },
+  { "c_atomic", &flag_isoc11 },
+  { "c_generic_selections", &flag_isoc11 },
+  { "c_static_assert", &flag_isoc11 },
+  { "c_thread_local", &flag_isoc11 },
+
+  /* XXX: Binary literals are available as a standard feature in
+     C2x.  They are standardised in C++14 and available as an extension
+     in all C versions.  Would it make more sense to have
+     cxx_binary_literals always report as an extension (in C) and add a
+     new c_binary_literals that reports as a feature for -std=c2x and an
+     extension below that?  */
+  { "cxx_binary_literals", &flag_isoc2x }
+};
+
+static bool
+c_has_feature (bool strict_p, const void *arg)
+{
+  auto info = static_cast <const c_feature_info *>(arg);
+  return !info->enable_flag || !strict_p || *info->enable_flag;
+}
+
+void
+c_register_features ()
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
+    {
+      const c_feature_info *info = c_feature_table + i;
+      c_common_register_feature (info->ident,
+				 c_has_feature,
+				 static_cast <const void *>(info));
+    }
+}
+
 bool
 c_missing_noreturn_ok_p (tree decl)
 {
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index 2f541460c4a..e3d65c8c8d2 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -104,6 +104,9 @@  static const char *cp_get_sarif_source_language (const char *);
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE cp_get_sarif_source_language
 
+#undef LANG_HOOKS_REGISTER_FEATURES
+#define LANG_HOOKS_REGISTER_FEATURES cp_register_features
+
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce..18f2a7d0fdc 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -23,10 +23,131 @@  along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "cp-tree.h"
 #include "cp-objcp-common.h"
+#include "c-family/c-common.h"
 #include "dwarf2.h"
 #include "stringpool.h"
 #include "contracts.h"
 
+struct cp_feature_selector
+{
+  enum
+  {
+    DIALECT,
+    FLAG
+  } kind;
+
+  union
+  {
+    const int *enable_flag;
+    struct {
+      enum cxx_dialect feat;
+      enum cxx_dialect ext;
+    } dialect;
+  };
+
+  constexpr cp_feature_selector (const int *flag)
+    : kind (FLAG), enable_flag (flag) {}
+  constexpr cp_feature_selector (enum cxx_dialect feat,
+				 enum cxx_dialect ext)
+    : kind (DIALECT), dialect{feat, ext} {}
+  constexpr cp_feature_selector (enum cxx_dialect feat)
+    : cp_feature_selector (feat, feat) {}
+
+  bool has_feature (bool strict_p) const;
+};
+
+bool cp_feature_selector::has_feature (bool strict_p) const
+{
+  switch (kind)
+    {
+    case DIALECT:
+      if (!strict_p)
+	return cxx_dialect >= dialect.ext;
+      return cxx_dialect >= dialect.feat;
+    case FLAG:
+      return *enable_flag;
+    }
+  gcc_unreachable ();
+}
+
+struct cp_feature_info
+{
+  const char *ident;
+  cp_feature_selector selector;
+};
+
+static const cp_feature_info cp_feature_table[] =
+{
+  { "cxx_exceptions", &flag_exceptions },
+  { "cxx_rtti", &flag_rtti },
+  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
+  { "cxx_alias_templates", cxx11 },
+  { "cxx_alignas", cxx11 },
+  { "cxx_alignof", cxx11 },
+  { "cxx_attributes", cxx11 },
+  { "cxx_constexpr", cxx11 },
+  { "cxx_constexpr_string_builtins", cxx11 },
+  { "cxx_decltype", cxx11 },
+  { "cxx_decltype_incomplete_return_types", cxx11 },
+  { "cxx_default_function_template_args", cxx11 },
+  { "cxx_defaulted_functions", cxx11 },
+  { "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 },
+};
+
+static bool
+cp_has_feature (bool strict_p, const void *arg)
+{
+  const auto info = static_cast <const cp_feature_info *>(arg);
+  return info->selector.has_feature (strict_p);
+}
+
+void
+cp_register_features ()
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
+    {
+      const cp_feature_info *info = cp_feature_table + i;
+      c_common_register_feature (info->ident,
+				 cp_has_feature,
+				 static_cast <const void *>(info));
+    }
+}
+
 /* 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..76dbb9892d6 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,33 @@  the operator is as follows:
 #endif
 @end smallexample
 
+@node @code{__has_feature}
+@subsection @code{__has_feature}
+@cindex @code{__has_feature}
+
+The special operator @code{__has_feature (@var{operand})} may be used in
+constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
+expressions to test whether the identifier given in @var{operand} is recognized
+as a feature supported by GCC given the current options and, in the case of
+standard language features, whether the feature is available in the chosen
+version of the language standard.
+
+@node @code{__has_extension}
+@subsection @code{__has_extension}
+@cindex @code{__has_extension}
+
+The special operator @code{__has_extension (@var{operand})} may be used in
+constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
+expressions to test whether the identifier given in @var{operand} is recognized
+as an extension supported by GCC given the current options.  In any given
+context, the features accepted by @code{__has_extension} are a strict superset
+of those accepted by @code{__has_feature}.  Unlike @code{__has_feature},
+@code{__has_extension} tests whether a given feature is available regardless of
+strict language standards conformance.
+
+If the @code{-pedantic-errors} flag is given, @code{__has_extension} is
+equivalent to @code{__has_feature}.
+
 @node @code{__has_include}
 @subsection @code{__has_include}
 @cindex @code{__has_include}
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index c6d18526360..46f9af02afa 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -151,6 +151,7 @@  extern const char *lhd_get_sarif_source_language (const char *);
 #define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location
 #define LANG_HOOKS_FINALIZE_EARLY_DEBUG lhd_finalize_early_debug
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language
+#define LANG_HOOKS_REGISTER_FEATURES lhd_do_nothing
 
 /* Attribute hooks.  */
 #define LANG_HOOKS_ATTRIBUTE_TABLE		NULL
@@ -394,7 +395,8 @@  extern void lhd_end_section (void);
   LANG_HOOKS_RUN_LANG_SELFTESTS, \
   LANG_HOOKS_GET_SUBSTRING_LOCATION, \
   LANG_HOOKS_FINALIZE_EARLY_DEBUG,   \
-  LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE \
+  LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE, \
+  LANG_HOOKS_REGISTER_FEATURES \
 }
 
 #endif /* GCC_LANG_HOOKS_DEF_H */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index cca75285fc2..009a03c0db6 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -643,6 +643,8 @@  struct lang_hooks
      languages.  */
   const char *(*get_sarif_source_language) (const char *filename);
 
+  void (*register_features) ();
+
   /* Whenever you add entries here, make sure you adjust langhooks-def.h
      and langhooks.cc accordingly.  */
 };
diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
index e4c49e664e1..e80dde07728 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)
     }
 }
 
+typedef bool (*objc_feature_p)();
+
+struct objc_feature_info
+{
+  const char *ident;
+  objc_feature_p predicate;
+
+  objc_feature_info (const char *name) : ident (name) {}
+  objc_feature_info (const char *name, objc_feature_p p)
+    : ident (name), predicate (p) {}
+
+  bool has_feature (bool) const
+    {
+      return predicate ? predicate () : true;
+    }
+};
+
+static bool objc_nonfragile_abi_p ()
+{
+  return flag_next_runtime && flag_objc_abi >= 2;
+}
+
+static bool objc_has_feature (bool strict_p, const void *arg)
+{
+  auto info = static_cast <const objc_feature_info *>(arg);
+  return info->has_feature (strict_p);
+}
+
+static const objc_feature_info objc_features[] =
+{
+  { "objc_default_synthesize_properties" },
+  { "objc_instancetype" },
+  { "objc_nonfragile_abi", objc_nonfragile_abi_p }
+};
+
+void
+objc_common_register_features ()
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++)
+    {
+      const objc_feature_info *info = objc_features + i;
+      c_common_register_feature (info->ident,
+				 objc_has_feature,
+				 static_cast <const void *>(info));
+    }
+}
 
 #include "gt-objc-objc-act.h"
diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
index e21ab52d8ca..799e289342e 100644
--- a/gcc/objc/objc-act.h
+++ b/gcc/objc/objc-act.h
@@ -28,6 +28,10 @@  const char *objc_printable_name (tree, int);
 int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
 void objc_common_init_ts (void);
 const char *objc_get_sarif_source_language (const char *);
+void objc_register_features ();
+
+/* 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..103b5ca0673 100644
--- a/gcc/objc/objc-lang.cc
+++ b/gcc/objc/objc-lang.cc
@@ -48,6 +48,8 @@  enum c_language_kind c_language = clk_objc;
 #define LANG_HOOKS_TREE_SIZE objc_common_tree_size
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
+#undef LANG_HOOKS_REGISTER_FEATURES
+#define LANG_HOOKS_REGISTER_FEATURES objc_register_features
 
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
@@ -58,6 +60,12 @@  objc_get_sarif_source_language (const char *)
   return "objectivec";
 }
 
+void objc_register_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..c1bf0914a47 100644
--- a/gcc/objcp/objcp-lang.cc
+++ b/gcc/objcp/objcp-lang.cc
@@ -30,6 +30,7 @@  along with GCC; see the file COPYING3.  If not see
 
 enum c_language_kind c_language = clk_objcxx;
 static void objcxx_init_ts (void);
+static void objcxx_register_features ();
 
 /* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h;
    consequently, there should be very few hooks below.  */
@@ -42,6 +43,8 @@  static void objcxx_init_ts (void);
 #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS objcxx_init_ts
+#undef LANG_HOOKS_REGISTER_FEATURES
+#define LANG_HOOKS_REGISTER_FEATURES objcxx_register_features
 
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
@@ -87,4 +90,11 @@  objcxx_init_ts (void)
   cp_common_init_ts ();
 }
 
+static void
+objcxx_register_features ()
+{
+  objc_common_register_features ();
+  cp_register_features ();
+}
+
 #include "gtype-objcp.h"
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 aef703f8111..3586e2e9399 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -755,6 +755,9 @@  struct cpp_callbacks
   /* Callback to determine whether a built-in function is recognized.  */
   int (*has_builtin) (cpp_reader *);
 
+  /* Callback to determine whether a feature is available.  */
+  int (*has_feature) (cpp_reader *, bool);
+
   /* Callback that can change a user lazy into normal macro.  */
   void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned);
 
@@ -959,7 +962,9 @@  enum cpp_builtin_type
   BT_HAS_STD_ATTRIBUTE,		/* `__has_c_attribute(x)' */
   BT_HAS_BUILTIN,		/* `__has_builtin(x)' */
   BT_HAS_INCLUDE,		/* `__has_include(x)' */
-  BT_HAS_INCLUDE_NEXT		/* `__has_include_next(x)' */
+  BT_HAS_INCLUDE_NEXT,		/* `__has_include_next(x)' */
+  BT_HAS_FEATURE,		/* `__has_feature(x)' */
+  BT_HAS_EXTENSION		/* `__has_extension(x)' */
 };
 
 #define CPP_HASHNODE(HNODE)	((cpp_hashnode *) (HNODE))
diff --git a/libcpp/init.cc b/libcpp/init.cc
index 693feaa31ed..b7081db3907 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)