This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/C6000-CGT: Enumerations in CGT 8.3.x

Part Number: C6000-CGT

Tool/software: TI C/C++ Compiler

Hello,

We're testing new compiler release 8.3.4 and verified (also with CGT 8.3.6), that there is compilation failure with some specific circumstances, which didn't appear with CGT7. I'm attaching sample code (with command to build) to reproduce issue:

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

unsigned fun(X param)
{
    const unsigned int y[] = { param };
    
    return y[0];
}

int main()
{
    unsigned i = 0;
    
    i = fun(c);
    
    if(i>0)
    {
        ;
    }
}

enum_issue_for_TI.zip

Compiler error:

"./main.cpp", line 10: error #2721-D: invalid narrowing conversion from "X" to "unsigned int"
1 error detected in the compilation of "./main.cpp".

>> Compilation failure

Deeper investigation was made with online compilers from wandbox to have reference results (both GCC/Clang checked). Initial reference code:

#include<iostream>
#include<limits.h>


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

void fun(X param)
{
    const unsigned int y[] = { param };
    
    std::cout << "Value of y=" << y[0] << std::endl;
}

int main()
{
    const unsigned int x[] = { a, b, c};
    
    fun(c);
    
    std::cout << "Values: " << x[0] << ", " << sizeof(a) << ", " << sizeof(X) << std::endl;
}

with output:

Value of y=3
Values: 1, 4, 4
0

Error we observed with CGT was introduced by C++11 - narrowing conversion for array initialization means program is ill-formed (find "narrowing conversion" phrase here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf). To trigger this error,  code can be changed slightly:

#include<iostream>
#include<limits.h>


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

void fun(X param)
{
    const unsigned int y[] = { param };
    
    std::cout << "Value of y=" << y[0] << std::endl;
}

int main()
{
    const unsigned int x[] = { a, b, c};  // Works with b = 2, doesn't work with b = UINT_MAX
    
    fun(c); // Causes error.
    
    std::cout << "Values: " << x[0] << ", " << sizeof(a) << ", " << sizeof(X) << std::endl;
}

Error detected:

prog.cc: In function 'void fun(X)':
prog.cc:14:32: error: narrowing conversion of 'param' from 'long unsigned int' to 'unsigned int' [-Wnarrowing]
   14 |     const unsigned int y[] = { param };
      |                                ^~~~~
prog.cc: In function 'int main()':
prog.cc:21:39: error: narrowing conversion of 'c' from 'long unsigned int' to 'unsigned int' [-Wnarrowing]
   21 |     const unsigned int x[] = { a, b, c};  // Works with b = 2, doesn't work with b = UINT_MAX
      |                                       ^
1

To have well-formed program, it should be modified:

#include<iostream>
#include<limits.h>


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

void fun(X param)
{
    const unsigned long int y[] = { param };
    
    std::cout << "Value of y=" << y[0] << std::endl;
}

int main()
{
    const unsigned int x[] = { a };
    
    fun(c);
    
    std::cout << "Values: " << x[0] << ", " << sizeof(a) << ", " << sizeof(X) << std::endl;
}

With following output:
Value of y=4294967296
Values: 1, 8, 8
0
What is worth to notice is fact, that first error appears as warning, when warnings are not recognized as errors. Nevertheless, this example shows, how detection of narrowing conversion works in C++11.
 
Going back to sample code for CGT, which compiles easily with online compilers... It seems, that CGT8 is too restrictive, when function's parameter is enum type (assigning enum value directly to array doesn't trigger issue). When array type in line 10 is changed to int type, then it works. If last enum value is maximum value of int (2147483647), it still doesn't work, but when it is equal 2147483648 (range valid for unsigned int), issue disappears. We know, that in spec stays, that int type is used for enum representation, but it seems, that compiler incorrectly recognizes such code as narrowing conversion from int to unsigned (despite the fact, that for this specific enum precision isn't lost) and behaves in non-standard way.
 
We found out, that there is already one issue regarding enums reported to TI (CODEGEN-3740) and it seems, that issue described here could have the same root cause.
 
 
Best Regards,
ZD

  • user6284149 said:
    there is already one issue regarding enums reported to TI (CODEGEN-3740)

    I agree that issue is similar.  But I'm not certain it is the same problem.  So, I filed the issue EXT_EP-9680 to have your specific situation investigated.  You are welcome to track it with the link below in my signature.

    Thanks and regards,

    -George

  • Thank you. It is very important for us to get this correction (and rest of reported issues) together with RSA intrinsics (I assume, you know which request I'm reffering to), so all important fixes could be included in one compiler's release.

    Best Regrad,

    ZD

  • 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.

  • Hello,

    This answer is very detailed and clarifies all. As I checked here to ensure (Unscoped enumeration, point 1):

    https://en.cppreference.com/w/cpp/language/enum

    and directly in C++11 standard doc (chapter 7.2, point 7), for instance here:

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf

    It's implementation-defined, how enum is represented, so both CGT and GCC are allowed to do their own choice regarding enums. This is not a bug. And C++11 feature "enum with fixed underlaying type" you suggested is the solution for differences in project. Nothing more to discuss in this respect. Thank you for your support.

    Best Regards,

    ZD