diff options
author | Jason Merrill <jason@redhat.com> | 2024-08-29 11:09:21 -0400 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2024-09-05 21:29:55 -0400 |
commit | 1914ca8791ce4e0ba821e818cb6f86c76afdb6f2 (patch) | |
tree | 980003d03a14846312a2944faddf280a469a48a1 /gcc | |
parent | 3dafb65bb5c31b169dae180e0664dfcaee64afe6 (diff) | |
download | gcc-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.cc | 30 | ||||
-rw-r--r-- | gcc/c-family/c-common.h | 1 | ||||
-rw-r--r-- | gcc/c-family/c-warn.cc | 4 | ||||
-rw-r--r-- | gcc/c/c-objc-common.h | 1 | ||||
-rw-r--r-- | gcc/cp/cp-objcp-common.h | 1 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 7 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 11 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/attr-flag-enum-1.c | 37 |
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:; + } +} |