MISRA Discussion Forums

Full Version: 10.1: Can the underlying type of enum constants be unsigned?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
After reading the MISRA-C:2004 guideline and earlier posts to this
forum, I have concluded the following about enum:s:

* An enum object is in standard C "int", and hence has the underlying
type "int".

* An enum constant has the underlying type corresponding to the
magnitude, as specified by the table in 6.10.4

However, this leaves one open question: What is the sign of an enum
constant?

The background is that in embedded devices, peripheral units are often
memory mapped and described using a struct, for example:

Code:
/* Typical peripheral usage */
enum my_enum
{
  FIELD1 = 0x01U,
  FIELD2 = 0x02U,
};
typedef unsigned short my_uint16_t;
struct my_struct
{
  my_uint16_t x;
} s;

void test1(void);
void test2(enum my_enum v);
void do_something(void);

void test1(void)
{
  s.x = (FIELD1 | FIELD2);
}
void test2(enum my_enum v)
{
  if (v == FIELD1)
  {
    do_something();
  }
}
/* End of example */
Back to the question: do enum constans get the same signedness as the
initializer expression. In other words, can enum constants be
unsigned?



Alternative 1: Always signed

If we assume that the underlying type is signed, then the assignment
in "test1" will break rule 10.1, as the expression to the right has
the underlying type "signed char".

If this is the case, enum can't be used to describe bits in bitmask in
a MISRA-C-compliant application. This is a big drawback and would make
MISRA-C much less useful in real-world applications, as it would force
peripheral units to be described using preprocessor macros.



Alternative 2: Get type from the initializer expression

On the other hand, if the enum constants inherit the underlying type
from the initializer expression, it could be something like "unsigned
char". In which case, any operation involving an enum object (int) and
enum constants (e.g. unsigned char) appears to break rule 10.1, as
there will be an implicit conversion from "unsigned char" to "signed
int". For example, see the comparison in "test2".

Also, if this enum constant inherit the signedness from initializer
expression, what happens when the expression is left out, should it
inherit the type from the previous enum constant?

Code:
enum my_other_enum
{
  AnUnsignedConstant = 0x10U,
  IsThisSignedOrUnsigned
}


To conclude, I would like to know if enum constants should inherit the
signedness from the initializer expressions and, if so, should the
tools issue a MISRA-C error for expressions involving enum objects and
enum constants?

By the way, I work for a company providing a MISRA-C checker. In order
to make the tools as good as possible, I would like to get a straight
answer to the questions I ask in relation to rule 10.1 -- whether or
not the example code breaks other MISRA-C rules is irrelevant.
The C Standard says (C90, Section 6.5.2.2) that each enumerated type is compatible with an integer type but the choice of integer type is implementation-defined. Many implementations, including those for some popular embedded processors, choose the smallest type that can contain the range of values of the enumeration constants. This type may be signed or unsigned. So, an enum object may be an int but it may be some other integer type.

The C Standard also says (C90, Sections 6.1.3.3 and 6.5.2.2) that the enumeration constants have type int, regardless of the integer type that is used to represent their parent enumerated type. Section 6.5.2.2 also constrains enumeration constants to have a value that is representable as an int.

The underlying type of an enumeration constant is determined from the table in MISRA C:2004 Guidelines Section 6.10.4. But, note that the Guidelines say, also in Section 6.10.4, that if the actual type of the expression is (signed) int ,then the underlying type is the smallest signed integer type capable of representing the value. Since all enumeration constants have type int, the underlying type of all enumeration constants is signed.

The code example:

Code:
s.x = (FIELD1 | FIELD2);

violates Rule 10.1 because the right operand of = has underlying type signed char and this is implicitly converted to an unsigned 16-bit type. A type cast will be needed to avoid this violation. However, the code also violates Rule 12.7 because the bitwise OR operator is applied to operands with a signed underlying type. A better solution might therefore be to cast the enumeration constants to an unsigned type, e.g.

Code:
s.x = ((my_uint16_t)FIELD1 | (my_uint16_t)FIELD2);

It might be worth using macros to perform the casts and thereby provide unsigned versions of the enumeration constants, e.g.

Code:
#define FIELD1_U ((my_uint16_t)(FIELD1))

To recap the answers to the concluding questions:

Quote:To conclude, I would like to know if enum constants should inherit the signedness from the initializer expressions

No, enum constants are always signed.

Quote:and, if so, should the tools issue a MISRA-C error for expressions involving enum objects and enum constants?

That would depend on the nature of the expressions but code such as:

Code:
enum { A, B } e;

...

e = A;
if (A < B) { ... }

will not violate any arithmetic type rules.