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);
/* 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;
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))
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);
* Elif::
* @code{__has_attribute}::
* @code{__has_cpp_attribute}::
+* @code{__has_c_attribute}::
* @code{__has_builtin}::
* @code{__has_include}::
@end menu
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
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}
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
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 *);
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)' */
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),
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))
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:
{
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);