# V1084. The expression is always true/false. The value is out of range of enum values.

Анализатор обнаружил странное сравнение переменной перечисления с числом. Указанное число не входит в диапазон значений перечисления, поэтому такое сравнение не имеет смысла.

Если у перечисления указан нижележащий тип, то с переменной такого перечисления имеет смысл сравнивать только значения, которые можно уместить в этот тип.

Рассмотрим следующий пример:

    enum byte : unsigned char {}; // Range: [0; 255]
    void foo(byte b1)
    {
      if (b1 == 256)   // logic error : always false
      {
        //....
      }
    }

Перечисление 'byte' имеет нижележащий тип 'unsigned char'. Число 256 не вмещается в тип 'unsigned char', поэтому сравнение 'b1 == 256' всегда ложное.

Пример корректного сравнения:

    enum byte : unsigned char {}; // Range: [0; 255]
    void foo(byte b1)
    {
      if (b1 == 255)   // ok
      {
        //....
      }
    }

Более сложным случаем является перечисление без явного указания нижележащего типа.

Для языка C компилятор всегда использует тип 'int' в качестве нижележащего типа. Диапазоном значений перечисления будет весь диапазон 'int'.

Для языка C++ компилятор подставит в качестве нижележащего типа 'int' для строго типизированных перечислений (scoped enum). Диапазоном значений такого перечисления также будет весь диапазон 'int'.

Для обычных перечислений вычисление диапазона значений и нижележащего типа перечисления происходит особым образом. Согласно стандарту С++, компилятор выведет нижележащий тип на основе значений констант перечисления, пытаясь уместить их в следующие типы:

    int -> unsigned int -> long -> unsigned long ->
     long long -> unsigned long long

При этом внутри выбранного типа компилятор использует минимально необходимое число бит (n), способное уместить весь диапазон констант в перечислении. Такие перечисления смогут обрабатывать диапазон значений [- (2 ^ n) / 2; (2 ^ n) / 2 - 1] для знакового и [0; (2 ^ n) - 1] для беззнакового нижележащего типа соответственно.

Поэтому следующий код на языке C++ содержит ошибку, если используется компилятор, отличный от MSVC (например, GCC или Clang):

    enum EN { low = 2, high = 4 }; // Uses 3 bits, range: [0; 7]
    void foo(EN en1)
    {
      if (en1 != 8)   // logic error : always true
      {
        //....
      }
    }

Согласно стандарту C++, нижележащим типом для этого перечисления выберется 'int'. Внутри этого типа компилятор использует минимальное количество битовых полей, которое сможет вместить в себя все значения enum-констант.

В данном случае для вмещения всех значений (2 = 0b010 и 4 = 0b100) понадобится минимум 3 бита, поэтому переменная типа 'EN' может вместить в себя числа от 0 (0b000) до 7 (0b111) включительно. Число 8 занимает уже четыре бита (0b1000), поэтому в тип 'EN' оно уже не вмещается. Чтобы исправить ошибку, можно явно указать нижележащий тип:

    enum EN : int32_t { low = 2, high = 4 };
    // Now range is: [−2 147 483 648, 2 147 483 647]
    void foo(EN en1)
    {
      if (en1 != 8)   // ok
      {
        //....
      }
    }

Не все C++ компиляторы рассчитывают фактический размер перечисления согласно стандарту. Например, MSVC при компиляции C++ кода отходит от стандарта и рассчитывает размер перечисления в целях обратной совместимости по правилам языка C. Поэтому в качестве нижележащего типа MSVC всегда использует тип 'int', если специально не указан иной тип. В таком случае диапазоном значений перечисления будет диапазон 'int'. Поэтому в рассмотренном выше примере нет ошибки, если вы используете MSVC:

    enum EN { low = 2, high = 4 };
    // MSVC will use int as underlying type
    // range is: [−2 147 483 648, 2 147 483 647]

    void foo(EN en1)
    {
      if (en1 != 8)   // no logic error
      {
        //....
      }
    }

Однако писать такой код не стоит, потому что он будет непереносим на другие компиляторы. Следует явно указать 'int' в качестве нижележащего типа.

Если вы используете только компилятор MSVC и вас не интересует переносимость на другие компиляторы, то вы можете отключить предупреждения диагностики о непереносимости кода с помощью комментария:

    //-V1084_TURN_OFF_ON_MSVC

Предупреждения V1084, актуальные для MSVC, останутся.

Данная диагностика классифицируется как:

CWE-1024 CERT-EXP62-CPP