Skip to content

Commit 06280a9

Browse files
acoplan-armiains
andcommitted
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 <[email protected]>
1 parent d9abaa8 commit 06280a9

26 files changed

+884
-6
lines changed

gcc/c-family/c-common.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,44 @@ const struct fname_var_t fname_vars[] =
311311
{NULL, 0, 0},
312312
};
313313

314+
/* Flags to restrict availability of generic features that
315+
are known to __has_{feature,extension}. */
316+
317+
enum
318+
{
319+
HF_FLAG_NONE = 0,
320+
HF_FLAG_EXT = 1, /* Available only as an extension. */
321+
HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags. */
322+
};
323+
324+
/* Info for generic features which can be queried through
325+
__has_{feature,extension}. */
326+
327+
struct hf_feature_info
328+
{
329+
const char *ident;
330+
unsigned flags;
331+
unsigned mask;
332+
};
333+
334+
/* Table of generic features which can be queried through
335+
__has_{feature,extension}. */
336+
337+
static constexpr hf_feature_info has_feature_table[] =
338+
{
339+
{ "address_sanitizer", HF_FLAG_SANITIZE, SANITIZE_ADDRESS },
340+
{ "thread_sanitizer", HF_FLAG_SANITIZE, SANITIZE_THREAD },
341+
{ "leak_sanitizer", HF_FLAG_SANITIZE, SANITIZE_LEAK },
342+
{ "hwaddress_sanitizer", HF_FLAG_SANITIZE, SANITIZE_HWADDRESS },
343+
{ "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED },
344+
{ "attribute_deprecated_with_message", HF_FLAG_NONE, 0 },
345+
{ "attribute_unavailable_with_message", HF_FLAG_NONE, 0 },
346+
{ "enumerator_attributes", HF_FLAG_NONE, 0 },
347+
{ "tls", HF_FLAG_NONE, 0 },
348+
{ "gnu_asm_goto_with_outputs", HF_FLAG_EXT, 0 },
349+
{ "gnu_asm_goto_with_outputs_full", HF_FLAG_EXT, 0 }
350+
};
351+
314352
/* Global visibility options. */
315353
struct visibility_flags visibility_options;
316354

@@ -9891,4 +9929,63 @@ c_strict_flex_array_level_of (tree array_field)
98919929
return strict_flex_array_level;
98929930
}
98939931

9932+
/* Map from identifiers to booleans. Value is true for features, and
9933+
false for extensions. Used to implement __has_{feature,extension}. */
9934+
9935+
using feature_map_t = hash_map <tree, bool>;
9936+
static feature_map_t *feature_map;
9937+
9938+
/* Register a feature for __has_{feature,extension}. FEATURE_P is true
9939+
if the feature identified by NAME is a feature (as opposed to an
9940+
extension). */
9941+
9942+
void
9943+
c_common_register_feature (const char *name, bool feature_p)
9944+
{
9945+
bool dup = feature_map->put (get_identifier (name), feature_p);
9946+
gcc_checking_assert (!dup);
9947+
}
9948+
9949+
/* Lazily initialize hash table for __has_{feature,extension},
9950+
dispatching to the appropriate front end to register language-specific
9951+
features. */
9952+
9953+
static void
9954+
init_has_feature ()
9955+
{
9956+
gcc_checking_assert (!feature_map);
9957+
feature_map = new feature_map_t;
9958+
9959+
for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
9960+
{
9961+
const hf_feature_info *info = has_feature_table + i;
9962+
9963+
if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask))
9964+
continue;
9965+
9966+
const bool feature_p = !(info->flags & HF_FLAG_EXT);
9967+
c_common_register_feature (info->ident, feature_p);
9968+
}
9969+
9970+
/* Register language-specific features. */
9971+
c_family_register_lang_features ();
9972+
}
9973+
9974+
/* If STRICT_P is true, evaluate __has_feature (IDENT).
9975+
Otherwise, evaluate __has_extension (IDENT). */
9976+
9977+
bool
9978+
has_feature_p (const char *ident, bool strict_p)
9979+
{
9980+
if (!feature_map)
9981+
init_has_feature ();
9982+
9983+
tree name = canonicalize_attr_name (get_identifier (ident));
9984+
bool *feat_p = feature_map->get (name);
9985+
if (!feat_p)
9986+
return false;
9987+
9988+
return !strict_p || *feat_p;
9989+
}
9990+
98949991
#include "gt-c-family-c-common.h"

gcc/c-family/c-common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,14 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level,
11261126
ATTRIBUTE_GCC_DIAG(5,0);
11271127
extern int c_common_has_attribute (cpp_reader *, bool);
11281128
extern int c_common_has_builtin (cpp_reader *);
1129+
extern int c_common_has_feature (cpp_reader *, bool);
1130+
1131+
/* Implemented by each front end in *-lang.cc. */
1132+
extern void c_family_register_lang_features ();
1133+
1134+
/* Implemented in c-family/c-common.cc. */
1135+
extern void c_common_register_feature (const char *, bool);
1136+
extern bool has_feature_p (const char *, bool);
11291137

11301138
extern bool parse_optimize_options (tree, bool);
11311139

gcc/c-family/c-lex.cc

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ init_c_lex (void)
8383
cb->read_pch = c_common_read_pch;
8484
cb->has_attribute = c_common_has_attribute;
8585
cb->has_builtin = c_common_has_builtin;
86+
cb->has_feature = c_common_has_feature;
8687
cb->get_source_date_epoch = cb_get_source_date_epoch;
8788
cb->get_suggestion = cb_get_suggestion;
8889
cb->remap_filename = remap_macro_filename;
@@ -452,16 +453,16 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax)
452453
return result;
453454
}
454455

455-
/* Callback for has_builtin. */
456+
/* Helper for __has_{builtin,feature,extension}. */
456457

457-
int
458-
c_common_has_builtin (cpp_reader *pfile)
458+
static const char *
459+
c_common_lex_availability_macro (cpp_reader *pfile, const char *builtin)
459460
{
460461
const cpp_token *token = get_token_no_padding (pfile);
461462
if (token->type != CPP_OPEN_PAREN)
462463
{
463464
cpp_error (pfile, CPP_DL_ERROR,
464-
"missing '(' after \"__has_builtin\"");
465+
"missing '(' after \"__has_%s\"", builtin);
465466
return 0;
466467
}
467468

@@ -481,7 +482,7 @@ c_common_has_builtin (cpp_reader *pfile)
481482
else
482483
{
483484
cpp_error (pfile, CPP_DL_ERROR,
484-
"macro \"__has_builtin\" requires an identifier");
485+
"macro \"__has_%s\" requires an identifier", builtin);
485486
if (token->type == CPP_CLOSE_PAREN)
486487
return 0;
487488
}
@@ -500,9 +501,38 @@ c_common_has_builtin (cpp_reader *pfile)
500501
break;
501502
}
502503

504+
return name;
505+
}
506+
507+
/* Callback for has_builtin. */
508+
509+
int
510+
c_common_has_builtin (cpp_reader *pfile)
511+
{
512+
const char *name = c_common_lex_availability_macro (pfile, "builtin");
513+
if (!name)
514+
return 0;
515+
503516
return names_builtin_p (name);
504517
}
505518

519+
/* Callback for has_feature. STRICT_P is true for has_feature and false
520+
for has_extension. */
521+
522+
int
523+
c_common_has_feature (cpp_reader *pfile, bool strict_p)
524+
{
525+
const char *builtin = strict_p ? "feature" : "extension";
526+
const char *name = c_common_lex_availability_macro (pfile, builtin);
527+
if (!name)
528+
return 0;
529+
530+
/* If -pedantic-errors is given, __has_extension is equivalent to
531+
__has_feature. */
532+
strict_p |= flag_pedantic_errors;
533+
return has_feature_p (name, strict_p);
534+
}
535+
506536

507537
/* Read a token and return its type. Fill *VALUE with its value, if
508538
applicable. Fill *CPP_FLAGS with the token's flags, if it is

gcc/c-family/c-ppoutput.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ init_pp_output (FILE *out_stream)
162162

163163
cb->has_attribute = c_common_has_attribute;
164164
cb->has_builtin = c_common_has_builtin;
165+
cb->has_feature = c_common_has_feature;
165166
cb->get_source_date_epoch = cb_get_source_date_epoch;
166167
cb->remap_filename = remap_macro_filename;
167168

gcc/c/c-lang.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ c_get_sarif_source_language (const char *)
6363
return "c";
6464
}
6565

66+
/* Implement c-family hook to register language-specific features for
67+
__has_{feature,extension}. */
68+
69+
void
70+
c_family_register_lang_features ()
71+
{
72+
c_register_features ();
73+
}
74+
6675
#if CHECKING_P
6776

6877
namespace selftest {

gcc/c/c-objc-common.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,39 @@ along with GCC; see the file COPYING3. If not see
3434
static bool c_tree_printer (pretty_printer *, text_info *, const char *,
3535
int, bool, bool, bool, bool *, const char **);
3636

37+
/* Info for C language features which can be queried through
38+
__has_{feature,extension}. */
39+
40+
struct c_feature_info
41+
{
42+
const char *ident;
43+
const int *enable_flag;
44+
};
45+
46+
static const c_feature_info c_feature_table[] =
47+
{
48+
{ "c_alignas", &flag_isoc11 },
49+
{ "c_alignof", &flag_isoc11 },
50+
{ "c_atomic", &flag_isoc11 },
51+
{ "c_generic_selections", &flag_isoc11 },
52+
{ "c_static_assert", &flag_isoc11 },
53+
{ "c_thread_local", &flag_isoc11 },
54+
{ "cxx_binary_literals", &flag_isoc23 }
55+
};
56+
57+
/* Register features specific to the C language. */
58+
59+
void
60+
c_register_features ()
61+
{
62+
for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
63+
{
64+
const c_feature_info *info = c_feature_table + i;
65+
const bool feat_p = !info->enable_flag || *info->enable_flag;
66+
c_common_register_feature (info->ident, feat_p);
67+
}
68+
}
69+
3770
bool
3871
c_missing_noreturn_ok_p (tree decl)
3972
{

gcc/c/c-objc-common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see
2121
#ifndef GCC_C_OBJC_COMMON
2222
#define GCC_C_OBJC_COMMON
2323

24+
/* Implemented in c-objc-common.cc. */
25+
extern void c_register_features ();
26+
2427
/* Lang hooks that are shared between C and ObjC are defined here. Hooks
2528
specific to C or ObjC go in c-lang.cc and objc/objc-lang.cc, respectively. */
2629

gcc/cp/cp-lang.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ objcp_tsubst_expr (tree /*t*/, tree /*args*/, tsubst_flags_t /*complain*/,
119119
return NULL_TREE;
120120
}
121121

122+
/* Implement c-family hook to add language-specific features
123+
for __has_{feature,extension}. */
124+
125+
void
126+
c_family_register_lang_features ()
127+
{
128+
cp_register_features ();
129+
}
130+
122131
static const char *
123132
cxx_dwarf_name (tree t, int verbosity)
124133
{

0 commit comments

Comments
 (0)