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:
Alex Coplan 2023-03-17 16:30:51 +00:00
parent d9abaa8d58
commit 06280a906c
26 changed files with 884 additions and 6 deletions

View file

@ -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"

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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 {

View file

@ -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)
{

View file

@ -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. */

View file

@ -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)
{

View file

@ -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

View file

@ -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);

View file

@ -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}

View file

@ -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"

View file

@ -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. */

View file

@ -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. */

View file

@ -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)
{

View 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

View 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;

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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))

View file

@ -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),

View file

@ -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)