On Wed, Sep 27, 2023 at 03:27:30PM +0100, Alex Coplan wrote:
> Hi,
> 
> This is a v4 patch to address Jason's feedback here:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> 
> w.r.t. v3 it just removes a comment now that some uncertainty around
> cxx_binary_literals has been resolved, and updates the documentation as
> suggested to point to the Clang docs.
> 
> ----------------------------------------------------------------------
> 
> This patch implements clang's __has_feature and __has_extension in GCC.
> Currently the patch aims to implement all documented features (and some
> undocumented ones) following the documentation at
> https://clang.llvm.org/docs/LanguageExtensions.html with the exception
> of the legacy features for C++ type traits.  These are omitted, since as
> the clang documentation notes, __has_builtin is the correct "modern" way
> to query for these (which GCC already implements).
> 
> Bootstrapped/regtested on aarch64-linux-gnu, bootstrapped on
> x86_64-apple-darwin, darwin regtest in progress.  OK for trunk if
> testing passes?

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

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

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

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

Not enum sanitize_code for mask?

> +};
> +
> +/* Table of generic features which can be queried through
> +   __has_{feature,extension}.  */
> +
> +static const hf_feature_info has_feature_table[] =
> +{
> +  { "address_sanitizer",         HF_FLAG_SANITIZE, SANITIZE_ADDRESS },
> +  { "thread_sanitizer",                  HF_FLAG_SANITIZE, SANITIZE_THREAD },
> +  { "leak_sanitizer",                    HF_FLAG_SANITIZE, SANITIZE_LEAK },
> +  { "hwaddress_sanitizer",       HF_FLAG_SANITIZE, SANITIZE_HWADDRESS },
> +  { "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED },
> +  { "attribute_deprecated_with_message",  0, 0 },
> +  { "attribute_unavailable_with_message", 0, 0 },
> +  { "enumerator_attributes",           0, 0 },
> +  { "tls", 0, 0 },

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

> +  { "gnu_asm_goto_with_outputs",       HF_FLAG_EXT, 0 },
> +  { "gnu_asm_goto_with_outputs_full",          HF_FLAG_EXT, 0 }
> +};
> +
>  /* Global visibility options.  */
>  struct visibility_flags visibility_options;
>  
> @@ -9808,4 +9845,63 @@ c_strict_flex_array_level_of (tree array_field)
>    return strict_flex_array_level;
>  }
>  
> +/* Map from identifiers to booleans.  Value is true for features, and
> +   false for extensions.  Used to implement __has_{feature,extension}.  */
> +
> +using feature_map_t = hash_map <tree, bool>;
> +static feature_map_t *feature_map = nullptr;

You don't need " = nullptr" here.

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

"front end"

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

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

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

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

> +{
> +  if (!feature_map)
> +    init_has_feature ();
> +
> +  bool *feat_p = feature_map->get (get_identifier (ident));
> +  if (!feat_p)
> +    return false;
> +
> +  return !strict_p || *feat_p;
> +}
> +
>  #include "gt-c-family-c-common.h"
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 1fdba7ef3ea..28ea8d63f5a 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -1126,6 +1126,20 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum 
> cpp_diagnostic_level,
>       ATTRIBUTE_GCC_DIAG(5,0);
>  extern int c_common_has_attribute (cpp_reader *, bool);
>  extern int c_common_has_builtin (cpp_reader *);
> +extern int c_common_has_feature (cpp_reader *, bool);
> +
> +/* Implemented by each frontend in *-lang.cc.  */

"front end"

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

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

> +extern void c_common_register_feature (const char *, bool);
> +extern bool has_feature_p (const char *, bool);
> +
> +/* Implemented in c/c-objc-common.cc.  */
> +extern void c_register_features ();
> +
> +/* Implemented in cp/cp-objcp-common.cc.  */
> +extern void cp_register_features ();
>  
>  extern bool parse_optimize_options (tree, bool);
>  
> diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
> index 5e457b78cdd..6a605f91858 100644
> --- a/gcc/c-family/c-lex.cc
> +++ b/gcc/c-family/c-lex.cc
> @@ -82,6 +82,7 @@ init_c_lex (void)
>    cb->read_pch = c_common_read_pch;
>    cb->has_attribute = c_common_has_attribute;
>    cb->has_builtin = c_common_has_builtin;
> +  cb->has_feature = c_common_has_feature;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
>    cb->get_suggestion = cb_get_suggestion;
>    cb->remap_filename = remap_macro_filename;
> @@ -453,16 +454,16 @@ c_common_has_attribute (cpp_reader *pfile, bool 
> std_syntax)
>    return result;
>  }
>  
> -/* Callback for has_builtin.  */
> +/* Helper for __has_{builtin,feature,extension}.  */
>  
> -int
> -c_common_has_builtin (cpp_reader *pfile)
> +static const char *
> +c_common_lex_availability_macro (cpp_reader *pfile, const char *builtin)
>  {
>    const cpp_token *token = get_token_no_padding (pfile);
>    if (token->type != CPP_OPEN_PAREN)
>      {
>        cpp_error (pfile, CPP_DL_ERROR,
> -              "missing '(' after \"__has_builtin\"");
> +              "missing '(' after \"__has_%s\"", builtin);

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

>        return 0;
>      }
>  
> @@ -482,7 +483,7 @@ c_common_has_builtin (cpp_reader *pfile)
>    else
>      {
>        cpp_error (pfile, CPP_DL_ERROR,
> -              "macro \"__has_builtin\" requires an identifier");
> +              "macro \"__has_%s\" requires an identifier", builtin);
>        if (token->type == CPP_CLOSE_PAREN)
>       return 0;
>      }
> @@ -501,9 +502,38 @@ c_common_has_builtin (cpp_reader *pfile)
>       break;
>      }
>  
> +  return name;
> +}
> +
> +/* Callback for has_builtin.  */
> +
> +int
> +c_common_has_builtin (cpp_reader *pfile)
> +{
> +  const char *name = c_common_lex_availability_macro (pfile, "builtin");
> +  if (!name)
> +    return 0;
> +
>    return names_builtin_p (name);
>  }
>  
> +/* Callback for has_feature.  STRICT_P is true for has_feature and false
> +   for has_extension.  */
> +
> +int
> +c_common_has_feature (cpp_reader *pfile, bool strict_p)
> +{
> +  const char *builtin = strict_p ? "feature" : "extension";
> +  const char *name = c_common_lex_availability_macro (pfile, builtin);
> +  if (!name)
> +    return 0;
> +
> +  /* If -pedantic-errors is given, __has_extension is equivalent to
> +     __has_feature.  */
> +  strict_p |= flag_pedantic_errors;
> +  return has_feature_p (name, strict_p);
> +}
> +
>  
>  /* Read a token and return its type.  Fill *VALUE with its value, if
>     applicable.  Fill *CPP_FLAGS with the token's flags, if it is
> diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc
> index 4aa2bef2c0f..a1488c6f086 100644
> --- a/gcc/c-family/c-ppoutput.cc
> +++ b/gcc/c-family/c-ppoutput.cc
> @@ -162,6 +162,7 @@ init_pp_output (FILE *out_stream)
>  
>    cb->has_attribute = c_common_has_attribute;
>    cb->has_builtin = c_common_has_builtin;
> +  cb->has_feature = c_common_has_feature;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
>    cb->remap_filename = remap_macro_filename;
>  
> diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc
> index b4e0c8cfb8a..11e7aaac2e3 100644
> --- a/gcc/c/c-lang.cc
> +++ b/gcc/c/c-lang.cc
> @@ -61,6 +61,15 @@ c_get_sarif_source_language (const char *)
>    return "c";
>  }
>  
> +/* Implement c-family hook to register language-specific features for
> +   __has_{feature,extension}.  */
> +
> +void
> +c_family_register_lang_features ()
> +{
> +  c_register_features ();
> +}
> +
>  #if CHECKING_P
>  
>  namespace selftest {
> diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
> index e4aed61ed00..fdff0cb999e 100644
> --- a/gcc/c/c-objc-common.cc
> +++ b/gcc/c/c-objc-common.cc
> @@ -34,6 +34,39 @@ along with GCC; see the file COPYING3.  If not see
>  static bool c_tree_printer (pretty_printer *, text_info *, const char *,
>                           int, bool, bool, bool, bool *, const char **);
>  
> +/* Info for C language features which can be queried through
> +   __has_{feature,extension}.  */
> +
> +struct c_feature_info
> +{
> +  const char *ident;
> +  const int *enable_flag;
> +};
> +
> +static const c_feature_info c_feature_table[] =
> +{
> +  { "c_alignas", &flag_isoc11 },
> +  { "c_alignof", &flag_isoc11 },
> +  { "c_atomic", &flag_isoc11 },
> +  { "c_generic_selections", &flag_isoc11 },
> +  { "c_static_assert", &flag_isoc11 },
> +  { "c_thread_local", &flag_isoc11 },
> +  { "cxx_binary_literals", &flag_isoc2x }
> +};
> +
> +/* Register features specific to the C language.  */
> +
> +void
> +c_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
> +    {
> +      const c_feature_info *info = c_feature_table + i;
> +      const bool feat_p = !info->enable_flag || *info->enable_flag;
> +      c_common_register_feature (info->ident, feat_p);
> +    }
> +}
> +
>  bool
>  c_missing_noreturn_ok_p (tree decl)
>  {
> diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
> index 2f541460c4a..84200a9a04a 100644
> --- a/gcc/cp/cp-lang.cc
> +++ b/gcc/cp/cp-lang.cc
> @@ -121,6 +121,15 @@ objcp_tsubst_copy_and_build (tree /*t*/,
>    return NULL_TREE;
>  }
>  
> +/* Implement c-family hook to add language-specific features
> +   for __has_{feature,extension}.  */
> +
> +void
> +c_family_register_lang_features ()
> +{
> +  cp_register_features ();
> +}
> +
>  static const char *
>  cxx_dwarf_name (tree t, int verbosity)
>  {
> diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
> index 93b027b80ce..ad8af5b401e 100644
> --- a/gcc/cp/cp-objcp-common.cc
> +++ b/gcc/cp/cp-objcp-common.cc
> @@ -23,10 +23,154 @@ along with GCC; see the file COPYING3.  If not see
>  #include "coretypes.h"
>  #include "cp-tree.h"
>  #include "cp-objcp-common.h"
> +#include "c-family/c-common.h"
>  #include "dwarf2.h"
>  #include "stringpool.h"
>  #include "contracts.h"
>  
> +/* Class to determine whether a given C++ langauge feature is available.

"language"

> +   Used to implement __has_{feature,extension}.  */
> +
> +struct cp_feature_selector
> +{
> +  enum
> +  {
> +    DIALECT,
> +    FLAG
> +  } kind;
> +
> +  enum class result
> +  {
> +    NONE,
> +    EXT,
> +    FEAT
> +  };
> +
> +  union
> +  {
> +    const int *enable_flag;
> +    struct {
> +      enum cxx_dialect feat;
> +      enum cxx_dialect ext;
> +    } dialect;
> +  };
> +
> +  constexpr cp_feature_selector (const int *flag)
> +    : kind (FLAG), enable_flag (flag) {}
> +  constexpr cp_feature_selector (enum cxx_dialect feat,
> +                              enum cxx_dialect ext)
> +    : kind (DIALECT), dialect{feat, ext} {}
> +  constexpr cp_feature_selector (enum cxx_dialect feat)
> +    : cp_feature_selector (feat, feat) {}
> +
> +  inline result has_feature () const;
> +};
> +
> +/* Check whether this language feature is available as a feature,
> +   extension, or not at all.  */
> +
> +cp_feature_selector::result
> +cp_feature_selector::has_feature () const
> +{
> +  switch (kind)
> +    {
> +    case DIALECT:
> +      if (cxx_dialect >= dialect.feat)
> +     return result::FEAT;
> +      else if (cxx_dialect >= dialect.ext)
> +     return result::EXT;
> +      else
> +     return result::NONE;
> +    case FLAG:
> +      return *enable_flag ? result::FEAT : result::NONE;
> +    }
> +
> +  gcc_unreachable ();
> +}
> +
> +/* Information about a C++ language feature which can be queried
> +   through __has_{feature,extension}.  IDENT is the name of the feature,
> +   and SELECTOR encodes how to compute whether the feature is available.  */
> +
> +struct cp_feature_info
> +{
> +  const char *ident;
> +  cp_feature_selector selector;
> +};
> +
> +/* Table of features for __has_{feature,extension}.  */
> +
> +static const cp_feature_info cp_feature_table[] =
> +{
> +  { "cxx_exceptions", &flag_exceptions },
> +  { "cxx_rtti", &flag_rtti },
> +  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
> +  { "cxx_alias_templates", cxx11 },
> +  { "cxx_alignas", cxx11 },
> +  { "cxx_alignof", cxx11 },
> +  { "cxx_attributes", cxx11 },
> +  { "cxx_constexpr", cxx11 },
> +  { "cxx_constexpr_string_builtins", cxx11 },
> +  { "cxx_decltype", cxx11 },
> +  { "cxx_decltype_incomplete_return_types", cxx11 },
> +  { "cxx_default_function_template_args", cxx11 },
> +  { "cxx_defaulted_functions", cxx11 },
> +  { "cxx_delegating_constructors", cxx11 },
> +  { "cxx_deleted_functions", cxx11 },
> +  { "cxx_explicit_conversions", cxx11 },
> +  { "cxx_generalized_initializers", cxx11 },
> +  { "cxx_implicit_moves", cxx11 },
> +  { "cxx_inheriting_constructors", cxx11 },
> +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> +  { "cxx_lambdas", cxx11 },
> +  { "cxx_local_type_template_args", cxx11 },
> +  { "cxx_noexcept", cxx11 },
> +  { "cxx_nonstatic_member_init", cxx11 },
> +  { "cxx_nullptr", cxx11 },
> +  { "cxx_override_control", cxx11 },
> +  { "cxx_reference_qualified_functions", cxx11 },
> +  { "cxx_range_for", cxx11 },
> +  { "cxx_raw_string_literals", cxx11 },
> +  { "cxx_rvalue_references", cxx11 },
> +  { "cxx_static_assert", cxx11 },
> +  { "cxx_thread_local", cxx11 },
> +  { "cxx_auto_type", cxx11 },
> +  { "cxx_strong_enums", cxx11 },
> +  { "cxx_trailing_return", cxx11 },
> +  { "cxx_unicode_literals", cxx11 },
> +  { "cxx_unrestricted_unions", cxx11 },
> +  { "cxx_user_literals", cxx11 },
> +  { "cxx_variadic_templates", { cxx11, cxx98 } },
> +  { "cxx_binary_literals", { cxx14, cxx98 } },
> +  { "cxx_contextual_conversions", { cxx14, cxx98 } },
> +  { "cxx_decltype_auto", cxx14 },
> +  { "cxx_aggregate_nsdmi", cxx14 },
> +  { "cxx_init_captures", cxx14 },
> +  { "cxx_generic_lambdas", cxx14 },
> +  { "cxx_relaxed_constexpr", cxx14 },
> +  { "cxx_return_type_deduction", cxx14 },
> +  { "cxx_variable_templates", cxx14 },
> +  { "modules", &flag_modules },
> +};
> +
> +/* Register C++ language features for __has_{feature,extension}.  */
> +
> +void
> +cp_register_features ()
> +{
> +  using result = cp_feature_selector::result;
> +
> +  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
> +    {
> +      const cp_feature_info *info = cp_feature_table + i;
> +      const auto res = info->selector.has_feature ();
> +      if (res == result::NONE)
> +     continue;
> +
> +      c_common_register_feature (info->ident, res == result::FEAT);
> +    }
> +}
> +
>  /* Special routine to get the alias set for C++.  */
>  
>  alias_set_type
> diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
> index 3f492b33470..9ab22a46899 100644
> --- a/gcc/doc/cpp.texi
> +++ b/gcc/doc/cpp.texi
> @@ -3199,6 +3199,8 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}.
>  * @code{__has_cpp_attribute}::
>  * @code{__has_c_attribute}::
>  * @code{__has_builtin}::
> +* @code{__has_feature}::
> +* @code{__has_extension}::
>  * @code{__has_include}::
>  @end menu
>  
> @@ -3561,6 +3563,45 @@ the operator is as follows:
>  #endif
>  @end smallexample
>  
> +@node @code{__has_feature}
> +@subsection @code{__has_feature}
> +@cindex @code{__has_feature}
> +
> +The special operator @code{__has_feature (@var{operand})} may be used in
> +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
> +expressions to test whether the identifier given in @var{operand} is 
> recognized
> +as a feature supported by GCC given the current options and, in the case of
> +standard language features, whether the feature is available in the chosen
> +version of the language standard.
> +
> +Note that @code{__has_feature} and @code{__has_extension} are not recommended
> +for use in new code, and are only provided for compatibility with Clang.  For
> +details of which identifiers are accepted by these function-like macros, see
> +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
> +the Clang documentation}}.
> +
> +@node @code{__has_extension}
> +@subsection @code{__has_extension}
> +@cindex @code{__has_extension}
> +
> +The special operator @code{__has_extension (@var{operand})} may be used in
> +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
> +expressions to test whether the identifier given in @var{operand} is 
> recognized
> +as an extension supported by GCC given the current options.  In any given
> +context, the features accepted by @code{__has_extension} are a strict 
> superset
> +of those accepted by @code{__has_feature}.  Unlike @code{__has_feature},
> +@code{__has_extension} tests whether a given feature is available regardless 
> of
> +strict language standards conformance.
> +
> +If the @code{-pedantic-errors} flag is given, @code{__has_extension} is

Should be @option{-pedantic-errors}.

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

I guess the void should be on a separate line.

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

I guess the void should be on a separate line.

> diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> index c0af82c0b1c..32282a37bee 100644
> --- a/libcpp/include/cpplib.h
> +++ b/libcpp/include/cpplib.h
> @@ -761,6 +761,9 @@ struct cpp_callbacks
>    /* Callback to determine whether a built-in function is recognized.  */
>    int (*has_builtin) (cpp_reader *);
>  
> +  /* Callback to determine whether a feature is available.  */
> +  int (*has_feature) (cpp_reader *, bool);
> +
>    /* Callback that can change a user lazy into normal macro.  */
>    void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned);
>  
> @@ -965,7 +968,9 @@ enum cpp_builtin_type
>    BT_HAS_STD_ATTRIBUTE,              /* `__has_c_attribute(x)' */
>    BT_HAS_BUILTIN,            /* `__has_builtin(x)' */
>    BT_HAS_INCLUDE,            /* `__has_include(x)' */
> -  BT_HAS_INCLUDE_NEXT                /* `__has_include_next(x)' */
> +  BT_HAS_INCLUDE_NEXT,               /* `__has_include_next(x)' */
> +  BT_HAS_FEATURE,            /* `__has_feature(x)' */
> +  BT_HAS_EXTENSION           /* `__has_extension(x)' */
>  };
>  
>  #define CPP_HASHNODE(HNODE)  ((cpp_hashnode *) (HNODE))
> diff --git a/libcpp/init.cc b/libcpp/init.cc
> index 9a20f8d8176..c1424d35949 100644
> --- a/libcpp/init.cc
> +++ b/libcpp/init.cc
> @@ -435,6 +435,8 @@ static const struct builtin_macro builtin_array[] =
>    B("__has_builtin",  BT_HAS_BUILTIN,   true),
>    B("__has_include",  BT_HAS_INCLUDE,   true),
>    B("__has_include_next",BT_HAS_INCLUDE_NEXT,   true),
> +  B("__has_feature",  BT_HAS_FEATURE, true),
> +  B("__has_extension",        BT_HAS_EXTENSION, true),
>    /* Keep builtins not used for -traditional-cpp at the end, and
>       update init_builtins() if any more are added.  */
>    B("_Pragma",                BT_PRAGMA,        true),
> diff --git a/libcpp/macro.cc b/libcpp/macro.cc
> index dada8fea835..f8b86d0fbfe 100644
> --- a/libcpp/macro.cc
> +++ b/libcpp/macro.cc
> @@ -677,6 +677,12 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode 
> *node,
>        number = builtin_has_include (pfile, node,
>                                   node->value.builtin == BT_HAS_INCLUDE_NEXT);
>        break;
> +
> +    case BT_HAS_FEATURE:
> +    case BT_HAS_EXTENSION:
> +      number = pfile->cb.has_feature (pfile,
> +                                   node->value.builtin == BT_HAS_FEATURE);
> +      break;
>      }
>  
>    if (result == NULL)

These bits look good too.

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

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

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

Thanks!

Marek

Reply via email to