Почему тип результата суммы двух целых чисел без знака отличается в Clang и GCC - PullRequest
0 голосов
/ 28 мая 2020

Я пишу код низкого уровня для своего эмулятора, который включает в себя множество 16- и 8-битных целых чисел без знака. Я включил предупреждение -Wconversion в своем проекте, и все предупреждения считаются ошибками (-Werror).

Рассмотрим этот фрагмент кода:

#include <cstdint>

int main ()
{
    uint16_t a = 4;
    uint16_t b = 6;
    uint16_t c = a + b;
}

До G CC 9.3 с -std=c++17 -Wconversion -Werror в качестве флагов компиляции дает следующую ошибку:

<source>: In function 'int main()':
<source>:7:20: error: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Werror=conversion]
    7 |     uint16_t c = a + b;

      |                  ~~^~~

Но тот же код не дает этой ошибки для GCC 10.1 и для любой версии компилятора Clang ( Тестировал до Clang 5.0.0). Ссылка на проводник компилятора.

Итак, мои вопросы следующие:

  • IMO, добавление двух unsigned ints не должно неявно преобразовываться в int. Или мое предположение неверно?
  • Почему Clang и GCC (до 9.3) дают разные результаты? Предусмотрены ли в стандарте какие-либо ограничения для этой операции или это зависит от поставщика компилятора?
  • Что изменилось в GCC 10.1? Почему эта ошибка не появляется для GCC 10.1?

Ответы [ 3 ]

4 голосов
/ 28 мая 2020

IMO, добавление двух беззнаковых целых чисел не должно быть неявно преобразовано в целое число. Или мое предположение неверно?

Это предположение не ошибочно. Беззнаковое int никогда неявно преобразуется в int.

Однако в вашей системе uint16_t бывает беззнаковым short int. Предположение, что короткие целые числа без знака неявно преобразованы в целые числа, является неправильным. В большинстве систем они повышаются до int.

Недавно был хороший вопрос о , почему продвижение должно быть подписано int: { ссылка }

Почему Clang и G CC (до 9.3) дают разные результаты?

Нет. Оба будут повышены до int. О конверсии просто не предупредили. Преобразование не является плохо оформленным, поэтому нет необходимости выдавать диагностику c.

, или это зависит от поставщика компилятора? Сообщения

Diagnosti c остаются на усмотрение поставщика компилятора (за исключением того, что сообщение Diagnosti c требуется, если программа имеет неправильный формат, если не указано иное).

Что изменилось в G CC 10.1? Почему эта ошибка не появляется для G CC 10.1?

Возможно, они решили, что предупреждение в данном случае бесполезно, и избавились от него.

4 голосов
/ 28 мая 2020

В вашем коде нет добавления двух unsigned int. Фактически вы добавляете два unsigned short, поскольку uint16_t является определением типа для unsigned short.

Правила целочисленного продвижения говорят, что любой целочисленный тип уже, чем int, который появляется как операнд + повышается до int (а не unsigned int, как вы могли ожидать).

Таким образом, задействованные шаги заключаются в том, что (int)4 добавляется к (int)6, давая (int)10, а затем присваивается обратно c.

1018 * Вы должны найти, что все компиляторы дают правильное значение c, поведение кода определена корректно. 1021 * поведение -Wconversion является более спорным. Поскольку этот код является прекрасным примером, он часто дает предупреждение для четко определенного кода. У этой проблемы нет очевидного решения, кроме как просто не использовать флаги или каким-то образом обернуть ложное срабатывание (возможно, #pragma или вызовом функции).

Некоторые люди действительно хотят видеть предупреждение для этого кода, а некоторые нет. Точный набор случаев, для которых предупреждение генерируется -Wconversion, продолжает меняться в g cc из-за того, что люди, отправляющие отчеты об ошибках, {do | not} хотят видеть предупреждение для некоторого конкретного случая.

0 голосов
/ 28 мая 2020

Я столкнулся с тем же предупреждением при выполнении битового сдвига:

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }

Вывод Clang с включенными -Wconversion предупреждениями:

<source>:10:53: warning: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Wimplicit-int-conversion]

uint8_t left_shift(uint8_t a, uint8_t b) { return a << b; }
                                           ~~~~~~ ~~^~~~

Но в листинге сборки нет никаких признаков фантомное продвижение int, на которое ссылаются другие в этой ветке. Используется только ширина byte:

https://godbolt.org/z/eSVaQW

left_shift(unsigned char, unsigned char):
        push    rbp
        mov     rbp, rsp
        mov     byte ptr [rbp - 1], dil
        mov     byte ptr [rbp - 2], sil
        movzx   eax, byte ptr [rbp - 1]
        movzx   ecx, byte ptr [rbp - 2]
        shl     eax, cl
        movzx   eax, al
        pop     rbp
        ret
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...