]> gcc.gnu.org Git - gcc.git/commitdiff
c-family: add attribute flag_enum [PR81665]
authorJason Merrill <jason@redhat.com>
Thu, 29 Aug 2024 15:09:21 +0000 (11:09 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 6 Sep 2024 01:29:55 +0000 (21:29 -0400)
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.

gcc/c-family/c-attribs.cc
gcc/c-family/c-common.h
gcc/c-family/c-warn.cc
gcc/c/c-objc-common.h
gcc/cp/cp-objcp-common.h
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/testsuite/c-c++-common/attr-flag-enum-1.c [new file with mode: 0644]
libstdc++-v3/include/bits/regex_constants.h

index 79303518dcb72662c8d367fba9c4047f8852e9c0..4dd2eecbea540528f30a2d2bf88d8f895c68d124 100644 (file)
@@ -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
index d3827573a3605124671336fa96a1c8abde215780..027f077d51bc5dbea813a364d04c89d59e7b03e9 100644 (file)
@@ -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.
index 5e4fb7f0f0a94a4acb4fb3faada505a0ff1fa496..47e0a6bfa07373ad53400da7fa80d58ce8ea5600 100644 (file)
@@ -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.
index 20af5a5bb94475b796c47ba7e7926dbf05247cd0..365b59388038ddd81fbc37c3ad9d9b9a4c46ec01 100644 (file)
@@ -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
 };
 
index 0e6664cf9c3e906a37c7dbd33913fdff3c9256f3..e9c5ac40020c9c30daac69f8de71df4bd995004e 100644 (file)
@@ -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
 };
 
index ebfa6779becbbf6bd441e16feb458f71d3b8fc5d..af0c45b42e07b856a9d7aa09b3ab404d24709b18 100644 (file)
@@ -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})
index 0f9b1bab19ff54b818548913c685e17733f6d93f..019e0a5ca80528fcf753d22ba9da5dfab3214471 100644 (file)
@@ -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 (file)
index 0000000..4eb78b1
--- /dev/null
@@ -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:;
+  }
+}
index 437895f1dc399f494a91b442b370ffd88b2f581e..4148093bc4e05900074d6a8d42bf9b2375657dcd 100644 (file)
@@ -66,7 +66,7 @@ namespace regex_constants
    * elements @c ECMAScript, @c basic, @c extended, @c awk, @c grep, @c egrep
    * %set.
    */
-  enum syntax_option_type : unsigned int
+  enum [[gnu::flag_enum]] syntax_option_type : unsigned int
   {
     _S_icase           = 1 << 0,
     _S_nosubs          = 1 << 1,
This page took 0.091161 seconds and 5 git commands to generate.