aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2024-08-29 11:09:21 -0400
committerJason Merrill <jason@redhat.com>2024-09-05 21:29:55 -0400
commit1914ca8791ce4e0ba821e818cb6f86c76afdb6f2 (patch)
tree980003d03a14846312a2944faddf280a469a48a1 /gcc
parent3dafb65bb5c31b169dae180e0664dfcaee64afe6 (diff)
downloadgcc-1914ca8791ce4e0ba821e818cb6f86c76afdb6f2.zip
gcc-1914ca8791ce4e0ba821e818cb6f86c76afdb6f2.tar.gz
gcc-1914ca8791ce4e0ba821e818cb6f86c76afdb6f2.tar.bz2
c-family: add attribute flag_enum [PR81665]
Several PRs complain about -Wswitch warning about a case for a bitwise combination of enumerators. Clang has an attribute flag_enum to prevent this; let's adopt that approach as well. This also recognizes the attribute as [[clang::flag_enum]], introducing handling of the clang attribute namespace. PR c++/46457 PR c++/81665 gcc/c-family/ChangeLog: * c-attribs.cc (handle_flag_enum_attribute): New. (c_common_gnu_attributes): Add it. (c_common_clang_attributes, c_common_clang_attribute_table): New. * c-common.h: Declare c_common_clang_attribute_table. * c-warn.cc (c_do_switch_warnings): Handle flag_enum. gcc/c/ChangeLog: * c-objc-common.h (c_objc_attribute_table): Add c_common_clang_attribute_table. gcc/cp/ChangeLog: * cp-objcp-common.h (cp_objcp_attribute_table): Add c_common_clang_attribute_table. gcc/testsuite/ChangeLog: * c-c++-common/attr-flag-enum-1.c: New test. gcc/ChangeLog: * doc/extend.texi: Document flag_enum attribute. * doc/invoke.texi: Mention flag_enum in -Wswitch. libstdc++-v3/ChangeLog: * include/bits/regex_constants.h: Use flag_enum.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c-attribs.cc30
-rw-r--r--gcc/c-family/c-common.h1
-rw-r--r--gcc/c-family/c-warn.cc4
-rw-r--r--gcc/c/c-objc-common.h1
-rw-r--r--gcc/cp/cp-objcp-common.h1
-rw-r--r--gcc/doc/extend.texi7
-rw-r--r--gcc/doc/invoke.texi11
-rw-r--r--gcc/testsuite/c-c++-common/attr-flag-enum-1.c37
8 files changed, 87 insertions, 5 deletions
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 7930351..4dd2eec 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -184,6 +184,7 @@ static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int,
static tree handle_hardbool_attribute (tree *, tree, tree, int, bool *);
static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
+static tree handle_flag_enum_attribute (tree *, tree, tree, int, bool *);
static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *);
/* Helper to define attribute exclusions. */
@@ -631,6 +632,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
handle_fd_arg_attribute, NULL},
{ "fd_arg_write", 1, 1, false, true, true, false,
handle_fd_arg_attribute, NULL},
+ { "flag_enum", 0, 0, false, true, false, false,
+ handle_flag_enum_attribute, NULL },
{ "null_terminated_string_arg", 1, 1, false, true, true, false,
handle_null_terminated_string_arg_attribute, NULL}
};
@@ -640,6 +643,17 @@ const struct scoped_attribute_specs c_common_gnu_attribute_table =
"gnu", { c_common_gnu_attributes }
};
+/* Attributes also recognized in the clang:: namespace. */
+const struct attribute_spec c_common_clang_attributes[] = {
+ { "flag_enum", 0, 0, false, true, false, false,
+ handle_flag_enum_attribute, NULL }
+};
+
+const struct scoped_attribute_specs c_common_clang_attribute_table =
+{
+ "clang", { c_common_clang_attributes }
+};
+
/* Give the specifications for the format attributes, used by C and all
descendants.
@@ -5025,6 +5039,22 @@ handle_fd_arg_attribute (tree *node, tree name, tree args,
return NULL_TREE;
}
+/* Handle the "flag_enum" attribute. */
+
+static tree
+handle_flag_enum_attribute (tree *node, tree ARG_UNUSED (name),
+ tree ARG_UNUSED (args), int ARG_UNUSED (flags),
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != ENUMERAL_TYPE)
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored on non-enum", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
/* Handle the "null_terminated_string_arg" attribute. */
static tree
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index d382757..027f077 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -821,6 +821,7 @@ extern struct visibility_flags visibility_options;
/* Attribute table common to the C front ends. */
extern const struct scoped_attribute_specs c_common_gnu_attribute_table;
+extern const struct scoped_attribute_specs c_common_clang_attribute_table;
extern const struct scoped_attribute_specs c_common_format_attribute_table;
/* Pointer to function to lazily generate the VAR_DECL for __FUNCTION__ etc.
diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index 5e4fb7f..47e0a6b 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -1808,6 +1808,10 @@ c_do_switch_warnings (splay_tree cases, location_t switch_location,
TREE_PURPOSE (chain));
}
+ /* Attribute flag_enum means bitwise combinations are OK. */
+ if (lookup_attribute ("flag_enum", TYPE_ATTRIBUTES (type)))
+ return;
+
/* Warn if there are case expressions that don't correspond to
enumerators. This can occur since C and C++ don't enforce
type-checking of assignments to enumeration variables.
diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h
index 20af5a5..365b593 100644
--- a/gcc/c/c-objc-common.h
+++ b/gcc/c/c-objc-common.h
@@ -79,6 +79,7 @@ static const scoped_attribute_specs *const c_objc_attribute_table[] =
{
&std_attribute_table,
&c_common_gnu_attribute_table,
+ &c_common_clang_attribute_table,
&c_common_format_attribute_table
};
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 0e6664c..e9c5ac4 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -128,6 +128,7 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
&std_attribute_table,
&cxx_gnu_attribute_table,
&c_common_gnu_attribute_table,
+ &c_common_clang_attribute_table,
&c_common_format_attribute_table
};
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ebfa677..af0c45b 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -9191,6 +9191,13 @@ initialization will result in future breakage.
GCC emits warnings based on this attribute by default; use
@option{-Wno-designated-init} to suppress them.
+@cindex @code{flag_enum} type attribute
+@item flag_enum
+This attribute may be applied to an enumerated type to indicate that
+its enumerators are used in bitwise operations, so e.g. @option{-Wswitch}
+should not warn about a @code{case} that corresponds to a bitwise
+combination of enumerators.
+
@cindex @code{hardbool} type attribute
@item hardbool
@itemx hardbool (@var{false_value})
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0f9b1ba..019e0a5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -7672,9 +7672,9 @@ unless C++14 mode (or newer) is active.
Warn whenever a @code{switch} statement has an index of enumerated type
and lacks a @code{case} for one or more of the named codes of that
enumeration. (The presence of a @code{default} label prevents this
-warning.) @code{case} labels outside the enumeration range also
-provoke warnings when this option is used (even if there is a
-@code{default} label).
+warning.) @code{case} labels that do not correspond to enumerators also
+provoke warnings when this option is used, unless the enumeration is marked
+with the @code{flag_enum} attribute.
This warning is enabled by @option{-Wall}.
@opindex Wswitch-default
@@ -7688,8 +7688,9 @@ case.
@item -Wswitch-enum
Warn whenever a @code{switch} statement has an index of enumerated type
and lacks a @code{case} for one or more of the named codes of that
-enumeration. @code{case} labels outside the enumeration range also
-provoke warnings when this option is used. The only difference
+enumeration. @code{case} labels that do not correspond to enumerators also
+provoke warnings when this option is used, unless the enumeration is marked
+with the @code{flag_enum} attribute. The only difference
between @option{-Wswitch} and this option is that this option gives a
warning about an omitted enumeration code even if there is a
@code{default} label.
diff --git a/gcc/testsuite/c-c++-common/attr-flag-enum-1.c b/gcc/testsuite/c-c++-common/attr-flag-enum-1.c
new file mode 100644
index 0000000..4eb78b1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-flag-enum-1.c
@@ -0,0 +1,37 @@
+/* { dg-additional-options -Wswitch } */
+
+enum E0 { a0 = 1, b0 = 2 };
+void f0 (enum E0 e) {
+ switch (e) {
+ case !(a0|b0): /* { dg-warning "not in enumerated type" } */
+ case a0|b0: /* { dg-warning "not in enumerated type" } */
+ default:;
+ }
+}
+
+enum __attribute ((flag_enum)) E1 { a1 = 1, b1 = 2 };
+void f1 (enum E1 e) {
+ switch (e) {
+ case !(a1|b1): /* { dg-bogus "not in enumerated type" } */
+ case a1|b1: /* { dg-bogus "not in enumerated type" } */
+ default:;
+ }
+}
+
+enum [[gnu::flag_enum]] E2 { a2 = 1, b2 = 2 };
+void f2 (enum E2 e) {
+ switch (e) {
+ case !(a2|b2): /* { dg-bogus "not in enumerated type" } */
+ case a2|b2: /* { dg-bogus "not in enumerated type" } */
+ default:;
+ }
+}
+
+enum [[clang::flag_enum]] E3 { a3 = 1, b3 = 2 };
+void f3 (enum E3 e) {
+ switch (e) {
+ case !(a3|b3): /* { dg-bogus "not in enumerated type" } */
+ case a3|b3: /* { dg-bogus "not in enumerated type" } */
+ default:;
+ }
+}