diff mbox series

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

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

Commit Message

Alex Coplan Aug. 3, 2023, 9:21 a.m. UTC
Hi,

This patch implements clang's __has_feature and __has_extension in GCC.
This is a v3 which addresses feedback for the v2 patch posted here:

https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html

Main changes since v2:
 - As per Jason's feedback, dropped the langhook in favour of
   a function prototyped in c-family/c-common.h and implemented in
   *-lang.cc for each frontend.
 - Also dropped the callbacks as suggested, we now compute whether
   features/extensions are available when __has_feature is first invoked,
   and only add available features to the hash table (storing a boolean
   to indicate whether a given identifier names a feature or an extension).
 - Added many comments to top-level definitions.
 - Generally polished and tidied up a bit.

As of this writing, there are still a couple of unresolved issues
around cxx_binary_literals and TLS, see:
https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html

Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin.
How does this version look?

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

Iain Sandoe Aug. 16, 2023, 7:37 p.m. UTC | #1
Hi Alex,
> On 3 Aug 2023, at 10:21, Alex Coplan <alex.coplan@arm.com> wrote:
> 

> This patch implements clang's __has_feature and __has_extension in GCC.
> This is a v3 which addresses feedback for the v2 patch posted here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html
> 
> Main changes since v2:
> - As per Jason's feedback, dropped the langhook in favour of
>   a function prototyped in c-family/c-common.h and implemented in
>   *-lang.cc for each frontend.
> - Also dropped the callbacks as suggested, we now compute whether
>   features/extensions are available when __has_feature is first invoked,
>   and only add available features to the hash table (storing a boolean
>   to indicate whether a given identifier names a feature or an extension).
> - Added many comments to top-level definitions.
> - Generally polished and tidied up a bit.
> 
> As of this writing, there are still a couple of unresolved issues
> around cxx_binary_literals and TLS, see:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html
> 
> Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin.
> How does this version look?

I smoke-tested this together with my current availability patch across a few
versions of Darwin and all is OK.  So fine for Objective-C/Darwin when the other
FE changes are approved,
thanks again for working on this,
Iain


> 
> 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.
> <patch.txt>
Alex Coplan Aug. 17, 2023, 9:39 a.m. UTC | #2
I'd like to ping this for review from C and C++ maintainers:
https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626178.html

I probably should have dropped the RFC tag this time round as I think
the patch is nearly ready, I suppose we just need agreement on the
issues below: is there any GCC configuration where __thread can get
rejected (I don't know of one), and should cxx_binary_literals report as
a feature with -std=c2x?

Thanks,
Alex

On 03/08/2023 10:21, Alex Coplan via Gcc-patches wrote:
> Hi,
> 
> This patch implements clang's __has_feature and __has_extension in GCC.
> This is a v3 which addresses feedback for the v2 patch posted here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html
> 
> Main changes since v2:
>  - As per Jason's feedback, dropped the langhook in favour of
>    a function prototyped in c-family/c-common.h and implemented in
>    *-lang.cc for each frontend.
>  - Also dropped the callbacks as suggested, we now compute whether
>    features/extensions are available when __has_feature is first invoked,
>    and only add available features to the hash table (storing a boolean
>    to indicate whether a given identifier names a feature or an extension).
>  - Added many comments to top-level definitions.
>  - Generally polished and tidied up a bit.
> 
> As of this writing, there are still a couple of unresolved issues
> around cxx_binary_literals and TLS, see:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html
> 
> Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin.
> How does this version look?
> 
> 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.
Alex Coplan Sept. 5, 2023, 10:33 a.m. UTC | #3
On 17/08/2023 10:39, Alex Coplan via Gcc-patches wrote:
> I'd like to ping this for review from C and C++ maintainers:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626178.html

Ping^2

> 
> I probably should have dropped the RFC tag this time round as I think
> the patch is nearly ready, I suppose we just need agreement on the
> issues below: is there any GCC configuration where __thread can get
> rejected (I don't know of one), and should cxx_binary_literals report as
> a feature with -std=c2x?
> 
> Thanks,
> Alex
Jason Merrill Sept. 19, 2023, 4:32 p.m. UTC | #4
On 8/3/23 05:21, Alex Coplan wrote:
> Hi,
> 
> This patch implements clang's __has_feature and __has_extension in GCC.
> This is a v3 which addresses feedback for the v2 patch posted here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html
> 
> Main changes since v2:
>   - As per Jason's feedback, dropped the langhook in favour of
>     a function prototyped in c-family/c-common.h and implemented in
>     *-lang.cc for each frontend.
>   - Also dropped the callbacks as suggested, we now compute whether
>     features/extensions are available when __has_feature is first invoked,
>     and only add available features to the hash table (storing a boolean
>     to indicate whether a given identifier names a feature or an extension).
>   - Added many comments to top-level definitions.
>   - Generally polished and tidied up a bit.
> 
> As of this writing, there are still a couple of unresolved issues
> around cxx_binary_literals and TLS, see:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626058.html

I think what you have for these makes sense.

> Bootstrapped/regtested on aarch64-linux-gnu and x86_64-apple-darwin.
> How does this version look?

> 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}.
> +

I think we need some documentation of what identifiers someone might 
specify.  It might be best to say that these are not recommended for new 
code, just provided for Clang compatibility, and point to their 
documentation.  I notice that we already refer to Clang docs for UBSan.

Jason
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 9fbaeb437a1..64ca6fa3d77 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;
 
@@ -9550,4 +9587,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 78fc5248ba6..54c916fc2ac 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 d8aa2907c51..8fd65d4507c 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..2a2363144ff 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -34,6 +34,46 @@  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 },
+
+  /* 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 }
+};
+
+/* 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..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/objc/objc-act.cc b/gcc/objc/objc-act.cc
index e4c49e664e1..4c10d4f8982 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 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)