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 (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 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 (info)); + } + + /* Register language-specific features. */ + lang_hooks.register_features (); +} + +bool +has_feature_p (const char *feat, bool strict_p) +{ + if (!feature_map) + init_has_feature (); + + const hf_table_entry *e = feature_map->get (get_identifier (feat)); + if (!e) + return false; + + return e->predicate (strict_p, e->info); +} + #include "gt-c-family-c-common.h" diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index b5ef5ff6b2c..5122767a8a5 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1123,6 +1123,15 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level, ATTRIBUTE_GCC_DIAG(5,0); extern int c_common_has_attribute (cpp_reader *, bool); extern int c_common_has_builtin (cpp_reader *); +extern int c_common_has_feature (cpp_reader *, bool); +extern bool has_feature_p (const char *, bool); + +typedef bool (*hf_predicate) (bool, const void *); +extern void c_common_register_feature (const char *, + hf_predicate, + const void *); +extern void c_register_features (); +extern void cp_register_features (); extern bool parse_optimize_options (tree, bool); diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index dcd061c7cb1..ddc8b349dbe 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -82,6 +82,7 @@ init_c_lex (void) cb->read_pch = c_common_read_pch; cb->has_attribute = c_common_has_attribute; cb->has_builtin = c_common_has_builtin; + cb->has_feature = c_common_has_feature; cb->get_source_date_epoch = cb_get_source_date_epoch; cb->get_suggestion = cb_get_suggestion; cb->remap_filename = remap_macro_filename; @@ -425,16 +426,16 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) return result; } -/* Callback for has_builtin. */ +/* Helper for __has_{builtin,feature,extension}. */ -int -c_common_has_builtin (cpp_reader *pfile) +static const char * +c_common_lex_availability_macro (cpp_reader *pfile, const char *builtin) { const cpp_token *token = get_token_no_padding (pfile); if (token->type != CPP_OPEN_PAREN) { cpp_error (pfile, CPP_DL_ERROR, - "missing '(' after \"__has_builtin\""); + "missing '(' after \"__has_%s\"", builtin); return 0; } @@ -454,7 +455,7 @@ c_common_has_builtin (cpp_reader *pfile) else { cpp_error (pfile, CPP_DL_ERROR, - "macro \"__has_builtin\" requires an identifier"); + "macro \"__has_%s\" requires an identifier", builtin); if (token->type == CPP_CLOSE_PAREN) return 0; } @@ -473,9 +474,38 @@ c_common_has_builtin (cpp_reader *pfile) break; } + return name; +} + +/* Callback for has_builtin. */ + +int +c_common_has_builtin (cpp_reader *pfile) +{ + const char *name = c_common_lex_availability_macro (pfile, "builtin"); + if (!name) + return 0; + return names_builtin_p (name); } +/* Callback for has_feature. STRICT_P is true for has_feature and false + for has_extension. */ + +int +c_common_has_feature (cpp_reader *pfile, bool strict_p) +{ + const char *builtin = strict_p ? "feature" : "extension"; + const char *name = c_common_lex_availability_macro (pfile, builtin); + if (!name) + return 0; + + /* If -pedantic-errors is given, __has_extension is equivalent to + __has_feature. */ + strict_p |= flag_pedantic_errors; + return has_feature_p (name, strict_p); +} + /* Read a token and return its type. Fill *VALUE with its value, if applicable. Fill *CPP_FLAGS with the token's flags, if it is diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc index 4aa2bef2c0f..a1488c6f086 100644 --- a/gcc/c-family/c-ppoutput.cc +++ b/gcc/c-family/c-ppoutput.cc @@ -162,6 +162,7 @@ init_pp_output (FILE *out_stream) cb->has_attribute = c_common_has_attribute; cb->has_builtin = c_common_has_builtin; + cb->has_feature = c_common_has_feature; cb->get_source_date_epoch = cb_get_source_date_epoch; cb->remap_filename = remap_macro_filename; diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc index b4e0c8cfb8a..e1b8bde31d7 100644 --- a/gcc/c/c-lang.cc +++ b/gcc/c/c-lang.cc @@ -49,6 +49,9 @@ enum c_language_kind c_language = clk_c; #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language +#undef LANG_HOOKS_REGISTER_FEATURES +#define LANG_HOOKS_REGISTER_FEATURES c_register_features + /* Each front end provides its own lang hook initializer. */ struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc index e4aed61ed00..1ab28f0ce12 100644 --- a/gcc/c/c-objc-common.cc +++ b/gcc/c/c-objc-common.cc @@ -34,6 +34,49 @@ along with GCC; see the file COPYING3. If not see static bool c_tree_printer (pretty_printer *, text_info *, const char *, int, bool, bool, bool, bool *, const char **); +struct c_feature_info +{ + const char *ident; + const int *enable_flag; +}; + +static const c_feature_info c_feature_table[] = +{ + { "c_alignas", &flag_isoc11 }, + { "c_alignof", &flag_isoc11 }, + { "c_atomic", &flag_isoc11 }, + { "c_generic_selections", &flag_isoc11 }, + { "c_static_assert", &flag_isoc11 }, + { "c_thread_local", &flag_isoc11 }, + + /* XXX: Binary literals are available as a standard feature in + C2x. They are standardised in C++14 and available as an extension + in all C versions. Would it make more sense to have + cxx_binary_literals always report as an extension (in C) and add a + new c_binary_literals that reports as a feature for -std=c2x and an + extension below that? */ + { "cxx_binary_literals", &flag_isoc2x } +}; + +static bool +c_has_feature (bool strict_p, const void *arg) +{ + auto info = static_cast (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 (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 (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 (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 (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 (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)