aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/c-family/c-common.h2
-rw-r--r--gcc/c-family/c-lex.c67
-rw-r--r--gcc/doc/cpp.texi21
-rw-r--r--gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c28
-rw-r--r--gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c41
-rw-r--r--gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c25
-rw-r--r--gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c18
-rw-r--r--libcpp/include/cpplib.h3
-rw-r--r--libcpp/init.c2
-rw-r--r--libcpp/macro.c6
-rw-r--r--libcpp/traditional.c1
11 files changed, 181 insertions, 33 deletions
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 94f4868..f470974 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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);
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index e81e16d..6cd3df7 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -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);
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 33f876a..291e146 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -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}
diff --git a/gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c
new file mode 100644
index 0000000..fe06abf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-1.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c
new file mode 100644
index 0000000..d6c4c6d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-2.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c
new file mode 100644
index 0000000..36842ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-3.c
@@ -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
diff --git a/gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c
new file mode 100644
index 0000000..acd35d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-has-c-attribute-4.c
@@ -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
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index c4d7cc5..8900e77 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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)' */
diff --git a/libcpp/init.c b/libcpp/init.c
index dcf1d4b..1b43802 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -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))
diff --git a/libcpp/macro.c b/libcpp/macro.c
index e2cb89e..aa16752 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -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:
diff --git a/libcpp/traditional.c b/libcpp/traditional.c
index b087072..225e3c2 100644
--- a/libcpp/traditional.c
+++ b/libcpp/traditional.c
@@ -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);