c: C2x __has_c_attribute
C2x adds the __has_c_attribute preprocessor operator, similar to C++ __has_cpp_attribute. GCC implements __has_cpp_attribute as exactly equivalent to __has_attribute. (The documentation says they differ regarding the values returned for standard attributes, but that's actually only a matter of the particular nonzero value returned not being specified in the documentation for __has_attribute; the implementation makes no distinction between the two.) I don't think having them exactly equivalent is actually correct, either for __has_cpp_attribute or for __has_c_attribute. Specifically, I think it is only correct for __has_cpp_attribute or __has_c_attribute to return nonzero if the given attribute is supported, with the particular pp-tokens passed to __has_cpp_attribute or __has_c_attribute, with [[]] syntax, not if it's only accepted in __attribute__ or with gnu:: added in [[]]. For example, they should return nonzero for gnu::packed, but zero for plain packed, because [[gnu::packed]] is accepted but [[packed]] is ignored as not a standard attribute. This patch implements that for __has_c_attribute, leaving any changes to __has_cpp_attribute for the C++ maintainers. A new BT_HAS_STD_ATTRIBUTE is added for __has_c_attribute (which I think, based on the above, would actually be correct to use for __has_cpp_attribute as well). The code in c_common_has_attribute that deals with scopes has its C++ conditional removed; instead, whether the language is C or C++ is used only to determine the numeric values returned for standard attributes (and which standard attributes are handled there at all). A new argument is passed to c_common_has_attribute to distinguish BT_HAS_STD_ATTRIBUTE from BT_HAS_ATTRIBUTE, and that argument is used to stop attributes with no scope specified from being accepted with __has_c_attribute unless they are one of the known standard attributes and so handled specially. Although the standard specify constants ending with 'L' as the values for the standard attributes, there is no correctness issue with the lack of code in GCC to add that 'L' to the expansion: __has_c_attribute and __has_cpp_attribute are expanded in #if after other macro expansion has occurred, with no semantics being specified if they occur outside #if, so there is no way for a conforming program to inspect the exact text of the expansion of those macros, only to use the resulting pp-number in a #if expression, where long and int have the same set of values. Bootstrapped with no regressions for x86_64-pc-linux-gnu. gcc/ 2020-11-12 Joseph Myers <joseph@codesourcery.com> * doc/cpp.texi (__has_attribute): Document when scopes are allowed for C. (__has_c_attribute): New. gcc/c-family/ 2020-11-12 Joseph Myers <joseph@codesourcery.com> * c-lex.c (c_common_has_attribute): Take argument std_syntax. Allow scope for C. Handle standard attributes for C. Do not accept unscoped attributes if std_syntax and not handled as standard attributes. * c-common.h (c_common_has_attribute): Update prototype. gcc/testsuite/ 2020-11-12 Joseph Myers <joseph@codesourcery.com> * gcc.dg/c2x-has-c-attribute-1.c, gcc.dg/c2x-has-c-attribute-2.c, gcc.dg/c2x-has-c-attribute-3.c, gcc.dg/c2x-has-c-attribute-4.c: New tests. libcpp/ 2020-11-12 Joseph Myers <joseph@codesourcery.com> * include/cpplib.h (struct cpp_callbacks): Add bool argument to has_attribute. (enum cpp_builtin_type): Add BT_HAS_STD_ATTRIBUTE. * init.c (builtin_array): Add __has_c_attribute. (cpp_init_special_builtins): Handle BT_HAS_STD_ATTRIBUTE. * macro.c (_cpp_builtin_macro_text): Handle BT_HAS_STD_ATTRIBUTE. Update call to has_attribute for BT_HAS_ATTRIBUTE. * traditional.c (fun_like_macro): Handle BT_HAS_STD_ATTRIBUTE.
This commit is contained in:
parent
6fcc3cac42
commit
1d00f8c863
11 changed files with 181 additions and 33 deletions
|
@ -1042,7 +1042,7 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level,
|
|||
enum cpp_warning_reason, rich_location *,
|
||||
const char *, va_list *)
|
||||
ATTRIBUTE_GCC_DIAG(5,0);
|
||||
extern int c_common_has_attribute (cpp_reader *);
|
||||
extern int c_common_has_attribute (cpp_reader *, bool);
|
||||
extern int c_common_has_builtin (cpp_reader *);
|
||||
|
||||
extern bool parse_optimize_options (tree, bool);
|
||||
|
|
|
@ -300,7 +300,7 @@ get_token_no_padding (cpp_reader *pfile)
|
|||
|
||||
/* Callback for has_attribute. */
|
||||
int
|
||||
c_common_has_attribute (cpp_reader *pfile)
|
||||
c_common_has_attribute (cpp_reader *pfile, bool std_syntax)
|
||||
{
|
||||
int result = 0;
|
||||
tree attr_name = NULL_TREE;
|
||||
|
@ -319,35 +319,37 @@ c_common_has_attribute (cpp_reader *pfile)
|
|||
attr_name = get_identifier ((const char *)
|
||||
cpp_token_as_text (pfile, token));
|
||||
attr_name = canonicalize_attr_name (attr_name);
|
||||
if (c_dialect_cxx ())
|
||||
bool have_scope = false;
|
||||
int idx = 0;
|
||||
const cpp_token *nxt_token;
|
||||
do
|
||||
nxt_token = cpp_peek_token (pfile, idx++);
|
||||
while (nxt_token->type == CPP_PADDING);
|
||||
if (nxt_token->type == CPP_SCOPE)
|
||||
{
|
||||
int idx = 0;
|
||||
const cpp_token *nxt_token;
|
||||
do
|
||||
nxt_token = cpp_peek_token (pfile, idx++);
|
||||
while (nxt_token->type == CPP_PADDING);
|
||||
if (nxt_token->type == CPP_SCOPE)
|
||||
have_scope = true;
|
||||
get_token_no_padding (pfile); // Eat scope.
|
||||
nxt_token = get_token_no_padding (pfile);
|
||||
if (nxt_token->type == CPP_NAME)
|
||||
{
|
||||
get_token_no_padding (pfile); // Eat scope.
|
||||
nxt_token = get_token_no_padding (pfile);
|
||||
if (nxt_token->type == CPP_NAME)
|
||||
{
|
||||
tree attr_ns = attr_name;
|
||||
tree attr_id
|
||||
= get_identifier ((const char *)
|
||||
cpp_token_as_text (pfile, nxt_token));
|
||||
attr_name = build_tree_list (attr_ns, attr_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
cpp_error (pfile, CPP_DL_ERROR,
|
||||
"attribute identifier required after scope");
|
||||
attr_name = NULL_TREE;
|
||||
}
|
||||
tree attr_ns = attr_name;
|
||||
tree attr_id
|
||||
= get_identifier ((const char *)
|
||||
cpp_token_as_text (pfile, nxt_token));
|
||||
attr_name = build_tree_list (attr_ns, attr_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Some standard attributes need special handling. */
|
||||
cpp_error (pfile, CPP_DL_ERROR,
|
||||
"attribute identifier required after scope");
|
||||
attr_name = NULL_TREE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Some standard attributes need special handling. */
|
||||
if (c_dialect_cxx ())
|
||||
{
|
||||
if (is_attribute_p ("noreturn", attr_name))
|
||||
result = 200809;
|
||||
else if (is_attribute_p ("deprecated", attr_name))
|
||||
|
@ -361,11 +363,20 @@ c_common_has_attribute (cpp_reader *pfile)
|
|||
result = 201803;
|
||||
else if (is_attribute_p ("nodiscard", attr_name))
|
||||
result = 201907;
|
||||
if (result)
|
||||
attr_name = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_attribute_p ("deprecated", attr_name)
|
||||
|| is_attribute_p ("maybe_unused", attr_name)
|
||||
|| is_attribute_p ("fallthrough", attr_name))
|
||||
result = 201904;
|
||||
else if (is_attribute_p ("nodiscard", attr_name))
|
||||
result = 202003;
|
||||
}
|
||||
if (result)
|
||||
attr_name = NULL_TREE;
|
||||
}
|
||||
if (attr_name)
|
||||
if (attr_name && (have_scope || !std_syntax))
|
||||
{
|
||||
init_attributes ();
|
||||
const struct attribute_spec *attr = lookup_attribute_spec (attr_name);
|
||||
|
|
|
@ -3159,6 +3159,7 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}.
|
|||
* Elif::
|
||||
* @code{__has_attribute}::
|
||||
* @code{__has_cpp_attribute}::
|
||||
* @code{__has_c_attribute}::
|
||||
* @code{__has_builtin}::
|
||||
* @code{__has_include}::
|
||||
@end menu
|
||||
|
@ -3432,8 +3433,9 @@ condition succeeds after the original @samp{#if} and all previous
|
|||
The special operator @code{__has_attribute (@var{operand})} may be used
|
||||
in @samp{#if} and @samp{#elif} expressions to test whether the attribute
|
||||
referenced by its @var{operand} is recognized by GCC. Using the operator
|
||||
in other contexts is not valid. In C code, @var{operand} must be
|
||||
a valid identifier. In C++ code, @var{operand} may be optionally
|
||||
in other contexts is not valid. In C code, if compiling for strict
|
||||
conformance to standards before C2x, @var{operand} must be
|
||||
a valid identifier. Otherwise, @var{operand} may be optionally
|
||||
introduced by the @code{@var{attribute-scope}::} prefix.
|
||||
The @var{attribute-scope} prefix identifies the ``namespace'' within
|
||||
which the attribute is recognized. The scope of GCC attributes is
|
||||
|
@ -3479,6 +3481,21 @@ information including the dates of the introduction of current standard
|
|||
attributes, see @w{@uref{https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations/,
|
||||
SD-6: SG10 Feature Test Recommendations}}.
|
||||
|
||||
@node @code{__has_c_attribute}
|
||||
@subsection @code{__has_c_attribute}
|
||||
@cindex @code{__has_c_attribute}
|
||||
|
||||
The special operator @code{__has_c_attribute (@var{operand})} may be
|
||||
used in @samp{#if} and @samp{#elif} expressions in C code to test
|
||||
whether the attribute referenced by its @var{operand} is recognized by
|
||||
GCC in attributes using the @samp{[[]]} syntax. GNU attributes must
|
||||
be specified with the scope @samp{gnu} or @samp{__gnu__} with
|
||||
@code{__has_c_attribute}. When @var{operand} designates a supported
|
||||
standard attribute it evaluates to an integer constant of the form
|
||||
@code{YYYYMM} indicating the year and month when the attribute was
|
||||
first introduced into the C standard, or when the syntax of operands
|
||||
to the attribute was extended in the C standard.
|
||||
|
||||
@node @code{__has_builtin}
|
||||
@subsection @code{__has_builtin}
|
||||
@cindex @code{__has_builtin}
|
||||
|
|
28
gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c
Normal file
28
gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* Test __has_c_attribute. Test basic properties. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#ifdef __has_c_attribute
|
||||
/* OK. */
|
||||
#else
|
||||
#error "__has_c_attribute not defined"
|
||||
#endif
|
||||
|
||||
#ifndef __has_c_attribute
|
||||
#error "__has_c_attribute not defined"
|
||||
#endif
|
||||
|
||||
#if defined __has_c_attribute
|
||||
/* OK. */
|
||||
#else
|
||||
#error "__has_c_attribute not defined"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute(foo)
|
||||
#error "foo attribute supported"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#elif __has_c_attribute(foo)
|
||||
#error "foo attribute supported"
|
||||
#endif
|
41
gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c
Normal file
41
gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* Test __has_c_attribute. Test supported attributes. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#if __has_c_attribute ( nodiscard ) != 202003L
|
||||
#error "bad result for nodiscard"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute ( __nodiscard__ ) != 202003L
|
||||
#error "bad result for __nodiscard__"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute(maybe_unused) != 201904L
|
||||
#error "bad result for maybe_unused"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute(__maybe_unused__) != 201904L
|
||||
#error "bad result for __maybe_unused__"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (deprecated) != 201904L
|
||||
#error "bad result for deprecated"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (__deprecated__) != 201904L
|
||||
#error "bad result for __deprecated__"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (fallthrough) != 201904L
|
||||
#error "bad result for fallthrough"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (__fallthrough__) != 201904L
|
||||
#error "bad result for __fallthrough__"
|
||||
#endif
|
||||
|
||||
/* Macros in the attribute name are expanded. */
|
||||
#define foo deprecated
|
||||
#if __has_c_attribute (foo) != 201904L
|
||||
#error "bad result for foo"
|
||||
#endif
|
25
gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c
Normal file
25
gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* Test __has_c_attribute. Test GNU attributes. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#if __has_c_attribute (gnu::packed) != 1
|
||||
#error "bad result for gnu::packed"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (__gnu__::__packed__) != 1
|
||||
#error "bad result for __gnu__::__packed__"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (gnu::__packed__) != 1
|
||||
#error "bad result for gnu::__packed__"
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (__gnu__::packed) != 1
|
||||
#error "bad result for __gnu__::packed"
|
||||
#endif
|
||||
|
||||
/* GNU attributes should not be reported as accepted without a scope
|
||||
specified. */
|
||||
#if __has_c_attribute (packed) != 0
|
||||
#error "bad result for packed"
|
||||
#endif
|
18
gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c
Normal file
18
gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* Test __has_c_attribute. Test syntax errors. */
|
||||
/* { dg-do preprocess } */
|
||||
/* { dg-options "-std=c2x -pedantic-errors" } */
|
||||
|
||||
#if __has_c_attribute /* { dg-error "missing '\\('" } */
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute 0 /* { dg-error "missing '\\('" } */
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (0 /* { dg-error "requires an identifier" } */
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (x /* { dg-error "missing '\\)'" } */
|
||||
#endif
|
||||
|
||||
#if __has_c_attribute (x::0) /* { dg-error "required after scope" } */
|
||||
#endif
|
|
@ -672,7 +672,7 @@ struct cpp_callbacks
|
|||
void (*used) (cpp_reader *, location_t, cpp_hashnode *);
|
||||
|
||||
/* Callback to identify whether an attribute exists. */
|
||||
int (*has_attribute) (cpp_reader *);
|
||||
int (*has_attribute) (cpp_reader *, bool);
|
||||
|
||||
/* Callback to determine whether a built-in function is recognized. */
|
||||
int (*has_builtin) (cpp_reader *);
|
||||
|
@ -857,6 +857,7 @@ enum cpp_builtin_type
|
|||
BT_TIMESTAMP, /* `__TIMESTAMP__' */
|
||||
BT_COUNTER, /* `__COUNTER__' */
|
||||
BT_HAS_ATTRIBUTE, /* `__has_attribute(x)' */
|
||||
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)' */
|
||||
|
|
|
@ -407,6 +407,7 @@ static const struct builtin_macro builtin_array[] =
|
|||
function-like macros in traditional.c:
|
||||
fun_like_macro() when adding more following */
|
||||
B("__has_attribute", BT_HAS_ATTRIBUTE, true),
|
||||
B("__has_c_attribute", BT_HAS_STD_ATTRIBUTE, true),
|
||||
B("__has_cpp_attribute", BT_HAS_ATTRIBUTE, true),
|
||||
B("__has_builtin", BT_HAS_BUILTIN, true),
|
||||
B("__has_include", BT_HAS_INCLUDE, true),
|
||||
|
@ -492,6 +493,7 @@ cpp_init_special_builtins (cpp_reader *pfile)
|
|||
for (b = builtin_array; b < builtin_array + n; b++)
|
||||
{
|
||||
if ((b->value == BT_HAS_ATTRIBUTE
|
||||
|| b->value == BT_HAS_STD_ATTRIBUTE
|
||||
|| b->value == BT_HAS_BUILTIN)
|
||||
&& (CPP_OPTION (pfile, lang) == CLK_ASM
|
||||
|| pfile->cb.has_attribute == NULL))
|
||||
|
|
|
@ -648,7 +648,11 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
|
|||
break;
|
||||
|
||||
case BT_HAS_ATTRIBUTE:
|
||||
number = pfile->cb.has_attribute (pfile);
|
||||
number = pfile->cb.has_attribute (pfile, false);
|
||||
break;
|
||||
|
||||
case BT_HAS_STD_ATTRIBUTE:
|
||||
number = pfile->cb.has_attribute (pfile, true);
|
||||
break;
|
||||
|
||||
case BT_HAS_BUILTIN:
|
||||
|
|
|
@ -330,6 +330,7 @@ fun_like_macro (cpp_hashnode *node)
|
|||
{
|
||||
if (cpp_builtin_macro_p (node))
|
||||
return (node->value.builtin == BT_HAS_ATTRIBUTE
|
||||
|| node->value.builtin == BT_HAS_STD_ATTRIBUTE
|
||||
|| node->value.builtin == BT_HAS_BUILTIN
|
||||
|| node->value.builtin == BT_HAS_INCLUDE
|
||||
|| node->value.builtin == BT_HAS_INCLUDE_NEXT);
|
||||
|
|
Loading…
Add table
Reference in a new issue