public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Alex Coplan <alex.coplan@arm.com>
To: gcc-patches@gcc.gnu.org, Jason Merrill <jason@redhat.com>,
	Nathan Sidwell <nathan@acm.org>,
	Joseph Myers <joseph@codesourcery.com>,
	Iain Sandoe <iain@sandoe.co.uk>
Subject: Re: [PATCH v2][RFC] c-family: Implement __has_feature and __has_extension [PR60512]
Date: Wed, 26 Jul 2023 15:00:44 +0100	[thread overview]
Message-ID: <ZMEnDLqBV5uhx5b/@arm.com> (raw)
In-Reply-To: <ZJwM98bEqz9tZ8kZ@arm.com>

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);
> +}
> +
>  \f
>  /* 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)

  reply	other threads:[~2023-07-26 14:01 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-28 10:35 Alex Coplan
2023-07-26 14:00 ` Alex Coplan [this message]
2023-07-26 20:26 ` Jason Merrill
2023-08-02 10:47   ` Alex Coplan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZMEnDLqBV5uhx5b/@arm.com \
    --to=alex.coplan@arm.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=iain@sandoe.co.uk \
    --cc=jason@redhat.com \
    --cc=joseph@codesourcery.com \
    --cc=nathan@acm.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).