Uploaded image for project: 'Embedded Software & Tools'
  1. Embedded Software & Tools
  2. EXT_EP-9680

Error about invalid narrowing conversion involving enum

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Won't Fix
    • Icon: Medium Medium
    • Code Generation Tools
    • CODEGEN-7225
    • C6000_8.3.6
    • C6000_8.3.7*
    • default
    • Hide
      There's a lot going on here, so please bear with me here.

      I'll start by saying that the diagnostic is not a bug; there is in fact a "narrowing conversion" as described in C++11. I'll explain why the diagnostic appears and how to adjust your code to work around it.

      Given your enum:

      typedef enum { a = 1, b, c } X;

      Unlike in C, the type X is an enumeration type, not an integer type. This is a subtle, but crucial, difference in expressions of mixed type. It is true that "under the hood," the compiler must choose an underlying integer type for the enumerated type in both C and C+, values and variables of type "X" are handled a bit differently than integer types in C+.

      The enumeration constants (a, b, c) range from [1, 3]. For reasons I won't get into in this thread, the compiler chooses the type "signed int" as the underlying type. Because this type is signed, it is possible to assign negative values to variables of type X. The compiler is not allowed to assume that variables of type X must only have one of the values a, b, or c.

      Now you have this line:

      const unsigned int y[] = { param };

      here "param" is of type X. Recall that X can have negative values, and "unsigned int" cannot represent negative values. In C+11 (dcl.init.list), "narrowing conversion" is defined (in part) as: "an implicit conversion [..] from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression [...]" Clearly this is a conversion from "X" (which is signed) to "unsigned int," thus it is a narrowing conversion. Note that a conversion in the other direction would also be narrowing; "signed int" cannot represent all values of "unsigned int." As you've discovered, the C+11 standard says that a list initializer with a "narrowing conversion" is "ill-formed."

      You can eliminate the diagnostic by changing this line:

      const unsigned int y[] = { param };

      to this:

      const X y[] = { param };

      This avoids the conversion, and thus avoids the diagnostic.

      Alternately, you can eliminate the diagnostic by using a fixed type (a C++11 feature) for the enum:

      typedef enum : unsigned int { a = 1, b, c } X;

      Now, why didn't you get this warning with GCC?

      For reasons I won't get into in this thread, GCC chose the type "unsigned int" for the underlying type, whereas the TI compiler chooses "signed int." Thus, for GCC, in the implicit conversion from "X" to "unsigned int," the destination is able to represent all of the values of "X", so it is not a narrowing conversion, so you don't get the diagnostic.

      When you set "b = UINT_MAX", you force "c" to be "UINT_MAX+1", which exceeds the size of "unsigned int", which causes the underlying type to be of the next higher size, "unsigned long", which for the GCC you are using is a 64-bit type. Now since not all values of "X" can be represented as an "unsigned int," (e.g. "c"), you will have a narrowing conversion and thus the diagnostic in the list initialization.
      Show
      There's a lot going on here, so please bear with me here. I'll start by saying that the diagnostic is not a bug; there is in fact a "narrowing conversion" as described in C++11. I'll explain why the diagnostic appears and how to adjust your code to work around it. Given your enum: typedef enum { a = 1, b, c } X; Unlike in C, the type X is an enumeration type, not an integer type. This is a subtle, but crucial, difference in expressions of mixed type. It is true that "under the hood," the compiler must choose an underlying integer type for the enumerated type in both C and C+, values and variables of type "X" are handled a bit differently than integer types in C+. The enumeration constants (a, b, c) range from [1, 3]. For reasons I won't get into in this thread, the compiler chooses the type "signed int" as the underlying type. Because this type is signed, it is possible to assign negative values to variables of type X. The compiler is not allowed to assume that variables of type X must only have one of the values a, b, or c. Now you have this line: const unsigned int y[] = { param }; here "param" is of type X. Recall that X can have negative values, and "unsigned int" cannot represent negative values. In C+11 (dcl.init.list), "narrowing conversion" is defined (in part) as: "an implicit conversion [..] from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression [...]" Clearly this is a conversion from "X" (which is signed) to "unsigned int," thus it is a narrowing conversion. Note that a conversion in the other direction would also be narrowing; "signed int" cannot represent all values of "unsigned int." As you've discovered, the C+11 standard says that a list initializer with a "narrowing conversion" is "ill-formed." You can eliminate the diagnostic by changing this line: const unsigned int y[] = { param }; to this: const X y[] = { param }; This avoids the conversion, and thus avoids the diagnostic. Alternately, you can eliminate the diagnostic by using a fixed type (a C++11 feature) for the enum: typedef enum : unsigned int { a = 1, b, c } X; Now, why didn't you get this warning with GCC? For reasons I won't get into in this thread, GCC chose the type "unsigned int" for the underlying type, whereas the TI compiler chooses "signed int." Thus, for GCC, in the implicit conversion from "X" to "unsigned int," the destination is able to represent all of the values of "X", so it is not a narrowing conversion, so you don't get the diagnostic. When you set "b = UINT_MAX", you force "c" to be "UINT_MAX+1", which exceeds the size of "unsigned int", which causes the underlying type to be of the next higher size, "unsigned long", which for the GCC you are using is a 64-bit type. Now since not all values of "X" can be represented as an "unsigned int," (e.g. "c"), you will have a narrowing conversion and thus the diagnostic in the list initialization.
    • User incorrectly predicted enumerated type's underlying type

      The diagnostic issued for this code is not a bug. There is in fact a "narrowing conversion" as described in C++11. Following is an explanation of why the diagnostic appears and how to adjust the code to prevent it.

      Given the enum:

      typedef enum

      { a = 1, b, c } X;

      Unlike in C, the type X is an enumeration type, not an integer type. This is a subtle, but crucial, difference in expressions of mixed type. It is true that "under the hood," the compiler must choose an underlying integer type for the enumerated type in both C and C+, values and variables of type "X" are handled a bit differently than integer types in C+.

      The enumeration constants (a, b, c) range from [1, 3]. For reasons I won't get into in this thread, the compiler chooses the type "signed int" as the underlying type. Because this type is signed, it is possible to assign negative values to variables of type X. The compiler is not allowed to assume that variables of type X must only have one of the values a, b, or c.

      Then there is this line:

      const unsigned int y[] = { param };

      here "param" is of type X. Recall that X can have negative values, and "unsigned int" cannot represent negative values. In C+11 (dcl.init.list), "narrowing conversion" is defined (in part) as: "an implicit conversion [..] from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression [...]" Clearly this is a conversion from "X" (which is signed) to "unsigned int," thus it is a narrowing conversion. Note that a conversion in the other direction would also be narrowing; "signed int" cannot represent all values of "unsigned int." The C+11 standard says that a list initializer with a "narrowing conversion" is "ill-formed."

      The diagnostic can be prevented by changing this line:

      const unsigned int y[] = { param };

      to this:

      const X y[] = { param };

      This avoids the conversion, and thus avoids the diagnostic.

      Alternately, the diagnostic can be eliminated by using a fixed type (a C++11 feature) for the enum:

      typedef enum : unsigned int { a = 1, b, c }

      X;

      GCC doesn't issue this warning because it chooses the type "unsigned int" for the underlying type, whereas the TI compiler chooses "signed int." Thus, for GCC, in the implicit conversion from "X" to "unsigned int," the destination is able to represent all of the values of "X", so it is not a narrowing conversion, so the diagnostic is not issued.

      When you set "b = UINT_MAX", you force "c" to be "UINT_MAX+1", which exceeds the size of "unsigned int", which causes the underlying type to be of the next higher size, "unsigned long", which for the GCC you are using is a 64-bit type. Now since not all values of "X" can be represented as an "unsigned int," (e.g. "c"), you will have a narrowing conversion and thus the diagnostic in the list initialization.

            syncuser TI User
            syncuser TI User
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: