c-family: Implement __has_feature and __has_extension [PR60512]
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). gcc/c-family/ChangeLog: PR c++/60512 * c-common.cc (struct hf_feature_info): New. (c_common_register_feature): New. (init_has_feature): New. (has_feature_p): New. * c-common.h (c_common_has_feature): New. (c_family_register_lang_features): New. (c_common_register_feature): New. (has_feature_p): New. * c-lex.cc (init_c_lex): Plumb through has_feature callback. (c_common_has_builtin): Generalize and move common part ... (c_common_lex_availability_macro): ... here. (c_common_has_feature): New. * c-ppoutput.cc (init_pp_output): Plumb through has_feature. gcc/c/ChangeLog: PR c++/60512 * c-lang.cc (c_family_register_lang_features): New. * c-objc-common.cc (struct c_feature_info): New. (c_register_features): New. * c-objc-common.h (c_register_features): New. gcc/cp/ChangeLog: PR c++/60512 * cp-lang.cc (c_family_register_lang_features): New. * cp-objcp-common.cc (struct cp_feature_selector): New. (cp_feature_selector::has_feature): New. (struct cp_feature_info): New. (cp_register_features): New. * cp-objcp-common.h (cp_register_features): New. gcc/ChangeLog: PR c++/60512 * doc/cpp.texi: Document __has_{feature,extension}. gcc/objc/ChangeLog: PR c++/60512 * objc-act.cc (struct objc_feature_info): New. (objc_nonfragile_abi_p): New. (objc_common_register_features): New. * objc-act.h (objc_common_register_features): New. * objc-lang.cc (c_family_register_lang_features): New. gcc/objcp/ChangeLog: PR c++/60512 * objcp-lang.cc (c_family_register_lang_features): New. libcpp/ChangeLog: PR c++/60512 * include/cpplib.h (struct cpp_callbacks): Add has_feature. (enum cpp_builtin_type): Add BT_HAS_{FEATURE,EXTENSION}. * init.cc: Add __has_{feature,extension}. * macro.cc (_cpp_builtin_macro_text): Handle BT_HAS_{FEATURE,EXTENSION}. gcc/testsuite/ChangeLog: PR c++/60512 * c-c++-common/has-feature-common.c: New test. * c-c++-common/has-feature-pedantic.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. Co-Authored-By: Iain Sandoe <iain@sandoe.co.uk>
This commit is contained in:
parent
d9abaa8d58
commit
06280a906c
26 changed files with 884 additions and 6 deletions
|
@ -311,6 +311,44 @@ 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_NONE = 0,
|
||||
HF_FLAG_EXT = 1, /* Available only as an extension. */
|
||||
HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags. */
|
||||
};
|
||||
|
||||
/* Info for generic features which can be queried through
|
||||
__has_{feature,extension}. */
|
||||
|
||||
struct hf_feature_info
|
||||
{
|
||||
const char *ident;
|
||||
unsigned flags;
|
||||
unsigned mask;
|
||||
};
|
||||
|
||||
/* Table of generic features which can be queried through
|
||||
__has_{feature,extension}. */
|
||||
|
||||
static constexpr 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", HF_FLAG_NONE, 0 },
|
||||
{ "attribute_unavailable_with_message", HF_FLAG_NONE, 0 },
|
||||
{ "enumerator_attributes", HF_FLAG_NONE, 0 },
|
||||
{ "tls", HF_FLAG_NONE, 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;
|
||||
|
||||
|
@ -9891,4 +9929,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;
|
||||
|
||||
/* 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 front end to register language-specific
|
||||
features. */
|
||||
|
||||
static void
|
||||
init_has_feature ()
|
||||
{
|
||||
gcc_checking_assert (!feature_map);
|
||||
feature_map = new feature_map_t;
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
|
||||
{
|
||||
const hf_feature_info *info = has_feature_table + i;
|
||||
|
||||
if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask))
|
||||
continue;
|
||||
|
||||
const bool feature_p = !(info->flags & HF_FLAG_EXT);
|
||||
c_common_register_feature (info->ident, feature_p);
|
||||
}
|
||||
|
||||
/* Register language-specific features. */
|
||||
c_family_register_lang_features ();
|
||||
}
|
||||
|
||||
/* If STRICT_P is true, evaluate __has_feature (IDENT).
|
||||
Otherwise, evaluate __has_extension (IDENT). */
|
||||
|
||||
bool
|
||||
has_feature_p (const char *ident, bool strict_p)
|
||||
{
|
||||
if (!feature_map)
|
||||
init_has_feature ();
|
||||
|
||||
tree name = canonicalize_attr_name (get_identifier (ident));
|
||||
bool *feat_p = feature_map->get (name);
|
||||
if (!feat_p)
|
||||
return false;
|
||||
|
||||
return !strict_p || *feat_p;
|
||||
}
|
||||
|
||||
#include "gt-c-family-c-common.h"
|
||||
|
|
|
@ -1126,6 +1126,14 @@ 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 front end in *-lang.cc. */
|
||||
extern void c_family_register_lang_features ();
|
||||
|
||||
/* Implemented in c-family/c-common.cc. */
|
||||
extern void c_common_register_feature (const char *, bool);
|
||||
extern bool has_feature_p (const char *, bool);
|
||||
|
||||
extern bool parse_optimize_options (tree, bool);
|
||||
|
||||
|
|
|
@ -83,6 +83,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;
|
||||
|
@ -452,16 +453,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;
|
||||
}
|
||||
|
||||
|
@ -481,7 +482,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;
|
||||
}
|
||||
|
@ -500,9 +501,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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -63,6 +63,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 {
|
||||
|
|
|
@ -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_isoc23 }
|
||||
};
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see
|
|||
#ifndef GCC_C_OBJC_COMMON
|
||||
#define GCC_C_OBJC_COMMON
|
||||
|
||||
/* Implemented in c-objc-common.cc. */
|
||||
extern void c_register_features ();
|
||||
|
||||
/* Lang hooks that are shared between C and ObjC are defined here. Hooks
|
||||
specific to C or ObjC go in c-lang.cc and objc/objc-lang.cc, respectively. */
|
||||
|
||||
|
|
|
@ -119,6 +119,15 @@ objcp_tsubst_expr (tree /*t*/, tree /*args*/, tsubst_flags_t /*complain*/,
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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++ language feature is available.
|
||||
Used to implement __has_{feature,extension}. */
|
||||
|
||||
struct cp_feature_selector
|
||||
{
|
||||
enum
|
||||
{
|
||||
DIALECT,
|
||||
FLAG
|
||||
} kind;
|
||||
|
||||
enum class result
|
||||
{
|
||||
NONE,
|
||||
EXT,
|
||||
FEAT
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
const int *enable_flag;
|
||||
struct {
|
||||
enum cxx_dialect feat;
|
||||
enum cxx_dialect ext;
|
||||
} dialect;
|
||||
};
|
||||
|
||||
constexpr cp_feature_selector (const int *flag)
|
||||
: kind (FLAG), enable_flag (flag) {}
|
||||
constexpr cp_feature_selector (enum cxx_dialect feat,
|
||||
enum cxx_dialect ext)
|
||||
: kind (DIALECT), dialect{feat, ext} {}
|
||||
constexpr cp_feature_selector (enum cxx_dialect feat)
|
||||
: cp_feature_selector (feat, feat) {}
|
||||
|
||||
inline result has_feature () const;
|
||||
};
|
||||
|
||||
/* Check whether this language feature is available as a feature,
|
||||
extension, or not at all. */
|
||||
|
||||
cp_feature_selector::result
|
||||
cp_feature_selector::has_feature () const
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case DIALECT:
|
||||
if (cxx_dialect >= dialect.feat)
|
||||
return result::FEAT;
|
||||
else if (cxx_dialect >= dialect.ext)
|
||||
return result::EXT;
|
||||
else
|
||||
return result::NONE;
|
||||
case FLAG:
|
||||
return *enable_flag ? result::FEAT : result::NONE;
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Information about a C++ language feature which can be queried
|
||||
through __has_{feature,extension}. IDENT is the name of the feature,
|
||||
and SELECTOR encodes how to compute whether the feature is available. */
|
||||
|
||||
struct cp_feature_info
|
||||
{
|
||||
const char *ident;
|
||||
cp_feature_selector selector;
|
||||
};
|
||||
|
||||
/* Table of features for __has_{feature,extension}. */
|
||||
|
||||
static constexpr 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
|
||||
|
|
|
@ -34,6 +34,7 @@ extern tree cp_classtype_as_base (const_tree);
|
|||
extern tree cp_get_global_decls ();
|
||||
extern tree cp_pushdecl (tree);
|
||||
extern void cp_register_dumps (gcc::dump_manager *);
|
||||
extern void cp_register_features ();
|
||||
extern bool cp_handle_option (size_t, const char *, HOST_WIDE_INT, int,
|
||||
location_t, const struct cl_option_handlers *);
|
||||
extern tree cxx_make_type_hook (tree_code);
|
||||
|
|
|
@ -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 @option{-pedantic-errors} flag is given, @code{__has_extension} is
|
||||
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}
|
||||
|
|
|
@ -10340,5 +10340,51 @@ objc_common_init_ts (void)
|
|||
MARK_TS_TYPED (PROPERTY_REF);
|
||||
}
|
||||
|
||||
/* 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 constexpr 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"
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -56,6 +56,16 @@ objc_get_sarif_source_language (const char *)
|
|||
return "objectivec";
|
||||
}
|
||||
|
||||
/* Implement c-family hook to add language-specific features
|
||||
for __has_{feature,extension}. */
|
||||
|
||||
void
|
||||
c_family_register_lang_features ()
|
||||
{
|
||||
objc_common_register_features ();
|
||||
c_register_features ();
|
||||
}
|
||||
|
||||
/* Lang hook routines common to C and ObjC appear in c-objc-common.cc;
|
||||
there should be very few (if any) routines below. */
|
||||
|
||||
|
|
|
@ -85,6 +85,16 @@ objcp_tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
#undef RECURSE
|
||||
}
|
||||
|
||||
/* Implement c-family hook to add language-specific features
|
||||
for __has_{feature,extension}. */
|
||||
|
||||
void
|
||||
c_family_register_lang_features ()
|
||||
{
|
||||
objc_common_register_features ();
|
||||
cp_register_features ();
|
||||
}
|
||||
|
||||
static void
|
||||
objcxx_init_ts (void)
|
||||
{
|
||||
|
|
73
gcc/testsuite/c-c++-common/has-feature-common.c
Normal file
73
gcc/testsuite/c-c++-common/has-feature-common.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* { 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 !EXT (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 !EXT (__gnu_asm_goto_with_outputs_full__)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (enumerator_attributes)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (__enumerator_attributes__)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (attribute_deprecated_with_message)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (__attribute_deprecated_with_message__)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (attribute_unavailable_with_message)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (__attribute_unavailable_with_message__)
|
||||
#error
|
||||
#endif
|
||||
|
||||
#if !FEAT (tls)
|
||||
#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
|
20
gcc/testsuite/c-c++-common/has-feature-pedantic.c
Normal file
20
gcc/testsuite/c-c++-common/has-feature-pedantic.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-additional-options "-pedantic-errors" } */
|
||||
|
||||
/* When -pedantic-errors is passed, __has_extension should behave like
|
||||
__has_feature. */
|
||||
|
||||
#if __has_feature (gnu_asm_goto_with_outputs)
|
||||
#error extension recognized as feature
|
||||
#endif
|
||||
|
||||
#if __has_extension (gnu_asm_goto_with_outputs)
|
||||
#error pure extensions should not be recognized with -pedantic-errors
|
||||
#endif
|
||||
|
||||
#if !__has_feature (tls) || !__has_extension (tls)
|
||||
#error features should still be recognized with -pedantic-errors
|
||||
#endif
|
||||
|
||||
/* Make this TU non-empty to appease -pedantic-errors. */
|
||||
int foo;
|
206
gcc/testsuite/g++.dg/ext/has-feature.C
Normal file
206
gcc/testsuite/g++.dg/ext/has-feature.C
Normal file
|
@ -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
|
6
gcc/testsuite/gcc.dg/asan/has-feature-asan.c
Normal file
6
gcc/testsuite/gcc.dg/asan/has-feature-asan.c
Normal file
|
@ -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
|
62
gcc/testsuite/gcc.dg/has-feature.c
Normal file
62
gcc/testsuite/gcc.dg/has-feature.c
Normal file
|
@ -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
|
6
gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c
Normal file
6
gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c
Normal file
|
@ -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
|
21
gcc/testsuite/obj-c++.dg/has-feature.mm
Normal file
21
gcc/testsuite/obj-c++.dg/has-feature.mm
Normal file
|
@ -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
|
26
gcc/testsuite/objc.dg/has-feature.m
Normal file
26
gcc/testsuite/objc.dg/has-feature.m
Normal file
|
@ -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
|
|
@ -764,6 +764,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);
|
||||
|
||||
|
@ -968,7 +971,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))
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue