From: Alex Coplan <alex.coplan@arm.com>
To: gcc-patches@gcc.gnu.org
Cc: Iain Sandoe <iain@sandoe.co.uk>,
Joseph Myers <joseph@codesourcery.com>,
Jason Merrill <jason@redhat.com>, Nathan Sidwell <nathan@acm.org>
Subject: [PATCH][RFC] c-family: Implement __has_feature and __has_extension [PR60512]
Date: Tue, 9 May 2023 13:07:11 +0100 [thread overview]
Message-ID: <ZFo3b8RDN8nseojl@arm.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 2387 bytes --]
Hi,
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 following
omissions:
- C++ type traits.
- Objective-C-specific features.
C++ type traits aren't currently implemented since, as the clang
documentation notes, __has_builtin is the correct "modern" way to query
for these (which GCC already implements). Of course there's an argument
that we should recognize the legacy set of C++ type traits that can be
queried through __has_feature for backwards compatibility with older
code. I'm happy to do this if reviewers think that's a good idea.
There are some comments in the patch marked with XXX, I'm looking for
review comments from C/C++ maintainers on those areas in particular.
Bootstrapped/regtested on aarch64-linux-gnu. Any comments?
Thanks,
Alex
gcc/c-family/ChangeLog:
PR c++/60512
* c-common.cc (struct hf_feature_info): New.
(has_generic_feature_p): New.
* c-common.h (c_common_has_feature): New.
(has_generic_feature_p): New.
(has_lang_feature_p): New.
* c-lex.cc (init_c_lex): Plumb through has_feature callback.
(c_common_has_builtin): Adapt into more generic helper function.
Rename to ...
(c_common_lex_availability_macro): ... this.
(c_common_has_feature): New.
* c-ppoutput.cc (init_pp_output): Plumb through has_feature callback.
gcc/c/ChangeLog:
PR c++/60512
* c-objc-common.cc (struct c_feature_info): New.
(has_lang_feature_p): New.
gcc/cp/ChangeLog:
PR c++/60512
* cp-objcp-common.cc (struct cp_feature_selector): New.
(cp_feature_selector::has_feature): New.
(struct cp_feature_info): New.
(has_lang_feature_p): New.
gcc/ChangeLog:
PR c++/60512
* doc/cpp.texi: Document __has_{feature,extension}.
libcpp/ChangeLog:
PR c++/60512
* include/cpplib.h (struct cpp_callbacks): Add has_feature callback.
(enum cpp_builtin_type): Add types for __has_{feature,extension}.
* init.cc (builtin_array): Add entries for __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.
* g++.dg/ext/has-feature.C: New test.
* gcc.dg/asan/has-feature-asan.c: New test.
* gcc.dg/has-feature.c: New test.
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 22248 bytes --]
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 2b4c82facf7..5b8429244b2 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;
@@ -9545,4 +9573,25 @@ c_strict_flex_array_level_of (tree array_field)
return strict_flex_array_level;
}
+bool
+has_generic_feature_p (const char *feat, bool strict_p)
+{
+ for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
+ {
+ const hf_feature_info *info = has_feature_table + i;
+ if (strcmp (info->ident, feat))
+ continue;
+
+ if ((info->flags & HF_FLAG_EXT) && strict_p)
+ return false;
+
+ if (info->flags & HF_FLAG_SANITIZE)
+ return flag_sanitize & info->mask;
+
+ return true;
+ }
+
+ return has_lang_feature_p (feat, strict_p);
+}
+
#include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f96350b64af..38fd30312a0 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1121,6 +1121,9 @@ 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_generic_feature_p (const char *, bool);
+extern bool has_lang_feature_p (const char *, bool);
extern bool parse_optimize_options (tree, bool);
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index 6eb0fae2f53..3301d90d6ab 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;
@@ -429,16 +430,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;
}
@@ -458,7 +459,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;
}
@@ -477,9 +478,35 @@ 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;
+
+ return has_generic_feature_p (name, strict_p);
+}
+
\f
/* 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-objc-common.cc b/gcc/c/c-objc-common.cc
index e4aed61ed00..30e6c491421 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -34,6 +34,48 @@ 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 }
+};
+
+bool
+has_lang_feature_p (const char *feat, bool strict_p)
+{
+ for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
+ {
+ const c_feature_info *info = c_feature_table + i;
+ if (strcmp (info->ident, feat))
+ continue;
+
+ if (info->enable_flag && strict_p && !*info->enable_flag)
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
bool
c_missing_noreturn_ok_p (tree decl)
{
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce..494f72b5ca1 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -23,10 +23,127 @@ 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 }, /* XXX: extension in c++98? */
+ { "cxx_delegating_constructors", { cxx11, cxx98 } },
+ { "cxx_deleted_functions", cxx11 },
+ { "cxx_explicit_conversions", { cxx11, cxx98 } },
+ { "cxx_generalized_initializers", cxx11 },
+ { "cxx_implicit_moves", cxx11 },
+ { "cxx_inheriting_constructors", cxx11 }, /* XXX: extension in c++98? */
+ { "cxx_inline_namespaces", { cxx11, cxx98 } },
+ { "cxx_lambdas", cxx11 }, /* XXX: extension in c++98? */
+ { "cxx_local_type_template_args", cxx11 },
+ { "cxx_noexcept", cxx11 },
+ { "cxx_nonstatic_member_init", { cxx11, cxx98 } },
+ { "cxx_nullptr", cxx11 },
+ { "cxx_override_control", { cxx11, cxx98 } },
+ { "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, cxx11 } },
+ { "cxx_generic_lambdas", cxx14 },
+ { "cxx_relaxed_constexpr", cxx14 },
+ { "cxx_return_type_deduction", cxx14 },
+ { "cxx_variable_templates", { cxx14, cxx98 } },
+ { "modules", &flag_modules },
+};
+
+bool
+has_lang_feature_p (const char *feat, bool strict_p)
+{
+ for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
+ {
+ const cp_feature_info *info = cp_feature_table + i;
+ if (strcmp (info->ident, feat))
+ continue;
+
+ return info->selector.has_feature (strict_p);
+ }
+
+ return false;
+}
+
/* 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 b0a2ce3ac6b..9ed96786249 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -3198,6 +3198,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
@@ -3560,6 +3562,30 @@ 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.
+
@node @code{__has_include}
@subsection @code{__has_include}
@cindex @code{__has_include}
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..58d0057c30d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/has-feature.C
@@ -0,0 +1,225 @@
+// { dg-do compile }
+
+#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_extension (cxx_delegating_constructors)
+#error
+#endif
+
+#if __has_feature (cxx_delegating_constructors) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_deleted_functions) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_explicit_conversions)
+#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_extension (cxx_nonstatic_member_init)
+#error
+#endif
+
+#if __has_feature (cxx_nonstatic_member_init) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_nullptr) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_override_control)
+#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_extension (cxx_variable_templates)
+#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..19d27699a92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/has-feature.c
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* 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
+
+enum { just_here_to_make_this_tu_nonempty };
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/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index a6f0abd894c..8f39a6a3a66 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 c508f06112a..465dafefe9d 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -433,6 +433,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 d4238d4f621..d2e8f9bd411 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)
next reply other threads:[~2023-05-09 12:07 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-09 12:07 Alex Coplan [this message]
2023-05-11 20:25 ` Jason Merrill
2023-05-11 21:26 ` Jonathan Wakely
2023-07-06 13:58 ` Alex Coplan
2023-05-14 16:05 ` Iain Sandoe
2023-06-20 12:30 ` Alex Coplan
2023-06-20 14:08 ` Iain Sandoe
2023-07-06 14:01 ` Alex Coplan
2023-07-06 15:23 ` Iain Sandoe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZFo3b8RDN8nseojl@arm.com \
--to=alex.coplan@arm.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=iain@sandoe.co.uk \
--cc=jason@redhat.com \
--cc=joseph@codesourcery.com \
--cc=nathan@acm.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).