https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81665

            Bug ID: 81665
           Summary: Please introduce flags attribute for enums which will
                    mimic one from C#
           Product: gcc
           Version: 5.4.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bugzi...@poradnik-webmastera.com
  Target Milestone: ---

Enums usually are used to define set of strongly-typed constants, and every
enum element is considered separate from others (assuming that their values are
different too). However sometimes they are also used as flags, which can be
or'ed together. This causes numerous problems:

- when two values are or'ed together and used as a label in switch, this
triggers compilation warning:
test-enum-op.c: In function ‘int main()’:
test-enum-op.c:25:5: warning: case value ‘6’ not in enumerated type ‘Values’
[-Wswitch]
     case Val2 | Val3:
     ^

- in C++ mode two enum values can be or'ed together, but resulting value will
have int type. This causes error when it is then assigned to variable of enum
type:
test-enum-op.c:19:23: error: invalid conversion from ‘int’ to ‘Values’
[-fpermissive]
     Values v = Val1 | Val2;
                       ^

- to eliminate above error, one has to define custom | operator for enum type.
However when it is not marked as a constexpr, it causes another compilation
error when its result is used as a case value:
test-enum-op.c:25:15: error: call to non-constexpr function ‘Values
operator|(Values, Values)’
     case Val2 | Val3:
               ^

- I also recall some tautological compare warning, that comparison is always
true or false. However I was not able to write code to demonstrate this here,
so maybe it was from clang.

It would be good to introduce __attribute__((flags)) which could be used with
enums. It should work in similar way as a [Flags] attribute in C#. It would
help to address issues listed above.

Tested on gcc 4.8.5 (Redhat 7) and 5.4.0 (Cygwin).

Code:

typedef enum Values
{
    Val_None = 0,
    Val1 = 1,
    Val2 = 2,
    Val3 = 4,
} Values;

#ifdef __cplusplus
constexpr
Values operator|(Values a, Values b)
{
    return static_cast<Values>(static_cast<unsigned>(a) |
static_cast<unsigned>(b));
}
#endif

int main()
{
    Values v = Val1 | Val2;

    switch(v)
    {
    case Val1:
        return 1;
    case Val2 | Val3:
        return 3;
    default:
        break;
    }

    if (v == -1)
        return 6;

    return 0;
}

Reply via email to