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