LINUX.ORG.RU

gcc дуркует и не может посчитать при компиляции арифметическое выражение из констант

 , ,


1

2

Как известно, в си препроцессор ничего не знает о константах уровня языка, потому, например, проверить условие sizeof(что-нибудь) > 8 при помощи #if не получится.

Чтобы делать compile time assert в си, есть вот такой прикол:

#define compile_time_assert(name, expr) \
	typedef int compile_time_assert_## name [1 - 2*!(expr)]

Эта штука не даёт коду скомпилироваться, если выражение expr вычислилось в 0 или если оно не может быть вычислено на этапе компиляции.

Например:

compile_time_assert(test1, 1);
compile_time_assert(test2, 2);
compile_time_assert(test3, 3);
compile_time_assert(test4, 4);
compile_time_assert(test5, 5);
compile_time_assert(test6, -1);
compile_time_assert(test7, -2);
compile_time_assert(test8, 10 == 10);

/* should fail, if uncommented: */
/* compile_time_assert(should_fail, 0); */

compile_time_assert(ancient_compiler_shall_not_pass, sizeof(int) >= 4);

----------------------------------------------

Это было вступление. Теперь, собственно, баг:

compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN);

Компилируется в tcc (0.9.27) и clang (7.0.0). В gcc (8.2.1) не компилируется. Компилятор считает, что выражение не может быть вычислено на этапе компиляции. (И это при том, что в предыдущем warning-е он уже его вычислил.) Вот что получается:

4.c:19:54: warning: integer overflow in expression '-2147483648' of type 'int' results in '-2147483648' [-Woverflow]
 compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN);
                                                      ^
4.c:5:51: note: in definition of macro 'compile_time_assert'
  typedef int compile_time_assert_## name [1 - 2*!(expr)]
                                                   ^~~~
4.c:5:14: error: variably modified 'compile_time_assert_twos_complement_math' at file scope
  typedef int compile_time_assert_## name [1 - 2*!(expr)]
              ^~~~~~~~~~~~~~~~~~~~
4.c:19:1: note: in expansion of macro 'compile_time_assert'
 compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN);

Это я что-то делаю не так, или это баг?

Потестируйте у себя, на каких версиях gcc воспроизводится, а на каких нет.

Deleted

Последнее исправление: Deleted (всего исправлений: 2)

Ну да, у тебя int overflow и неопределённое поведение.

Если ты это проверяешь потому, что надеешься, что всегда будет wraparound, ты это зря сделал.

proud_anon ★★★★★
()
Последнее исправление: proud_anon (всего исправлений: 1)

gcc 8.2.1 ворнингом поругался, но скомпилировал:

$ gcc q.c 
q.c: In function ‘main’:
q.c:10:54: warning: integer overflow in expression ‘-2147483648’ of type ‘int’ results in ‘-2147483648’ [-Woverflow]
 compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN);
                                                      ^
q.c:4:51: note: in definition of macro ‘compile_time_assert’
  typedef int compile_time_assert_## name [1 - 2*!(expr)]
                                                   ^~~~
sergej ★★★★★
()

Есть gcc 7.3.0 8.1.0 8.2.0. На всех не компилится. clang 7.0.0 и tcc 0.9.27 нормально.

imul ★★★★★
()
Ответ на: комментарий от sergej

Ворнинг то понятно: -INT_MIN - это переполнение. Мож у тебя альтернативные определения INT_MIN?

У меня

gcc 8.2.1+20180831-1

/usr/include/limits.h - glibc 2.28-4

sergej ★★★★★
()
Ответ на: комментарий от imul

у меня и с g++ не получается воспроизвести

константы беру из #include <climits> для C++ и из #include <limits.h> для C.

sergej ★★★★★
()

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

Iron_Bug ★★★★★
()

5.5.0 — компилируется.

P.S. Проще взять какой-нибудь online-компилятор, где есть много версий, и там посмотреть начиная с какой не работает.

xaizek ★★★★★
()
Ответ на: комментарий от proud_anon

Ну да, у тебя int overflow и неопределённое поведение.

А точно UB, а не implementation-defined? Я не нашел сходу нужного места в докУменте.

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

Deleted
()
Ответ на: комментарий от proud_anon

Если ты это проверяешь потому, что надеешься, что всегда будет wraparound, ты это зря сделал.

Да нет, я случайно наткнулся. Тут анон скинул ссылку на код из 70-х, где был фрагмент типа:

if (x < 0) {
   x = -x;
   if (x < 0) {
       

Я заинтересовался, что будет, если засунуть INT_MIN == - INT_MIN в современный компилятор. И вот получил не совсем тот результат, на который расчитывал.

Самый прикол, что если сделать просто if (INT_MIN == - INT_MIN) {printf("OK\n");}, код прекрасно компилируется. А вот статически вычислиться не хочет.

Deleted
()
4.c:5:14: error: variably modified 'compile_time_assert_twos_complement_math' at file scope
  typedef int compile_time_assert_## name [1 - 2*!(expr)]

Быть может INT_MIN определен в limits.h как

const int INT_MIN = /* значение */;

Грепни у себя.

KennyMinigun ★★★★★
()
Ответ на: комментарий от KennyMinigun

Ха, лол, ошибка воспроизводится в зависимости от способа определения INT_MIN:

Вау! Глубоко копнул. :)

Deleted
()
Ответ на: комментарий от Deleted

Если я тебя ни с кем не путаю, то ты уже года 2 не можешь найти нужного места в документе.

anonymous
()
Ответ на: комментарий от KennyMinigun

так автор жалуется, что вычисление констант «не работает». всё работает. если убрать сначала переполнение.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

так автор жалуется, что вычисление констант «не работает». всё работает. если убрать сначала переполнение.

Переполнение — отдельная тема. Конечно какашка, но на него есть ворнинг (и не более!).

Ворос: почему gcc считает, что -(-INT_MAX - 1) — это не константное значение. Т.к. ошибку error: variably modified ... at file scope вызывает именно подание размера глобального массива через (статическую) переменную. Следовательно и предположение, что gcc не посчитал выражение в compile-time.

Что забавно, g++ тот же пример компилирует без ошибок (с ворнингом конечно): https://wandbox.org/permlink/1y96wr2kN0u6WxEW

KennyMinigun ★★★★★
()

Это я что-то делаю не так, или это баг?

Это баг сишного стандарта. Он говорит, что результат константного выражения должен влезать в диапазон, представимый типом результата. Но ничего не говорит о подвыражениях константного выражения. Так что непонятно, что делать с переполнениями.

anonymous
()
Ответ на: комментарий от KennyMinigun

подание размера глобального массива через (статическую) переменную

А где там подание размера через переменную?

anonymous
()
Ответ на: комментарий от anonymous

А где там подание размера через переменную?

Вот в том то и дело, что его нет. А ошибка (соответствующая) есть.

KennyMinigun ★★★★★
()
Ответ на: комментарий от KennyMinigun

потому что по стандарту это UB. вот он и сообщает, что раз UB, то не может вычислить. приведение к long int всё ставит на места и вылезает ошибка отрицательного размера массива.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Ты как обычно нихрена не разбираешься, но мнение имеешь.

Приведение к long int там «после» UB и ничего на места не ставит.

anonymous
()
Ответ на: комментарий от Iron_Bug

потому что по стандарту это UB. вот он и сообщает, что раз UB, то не может вычислить.

Не могу опровергнуть, UB на то и UB что неизвестно. Но в другой форме то же самое UB gcc уже генерирует нормальную ошибку:

#include <limits.h>

#undef INT_MIN
#define INT_MIN -2147483648

#define compile_time_assert(name, expr) \
	typedef int compile_time_assert_## name [1 - 2*!(expr)]

compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN)

int main() {}

prog.c:7:14: error: size of array 'compile_time_assert_twos_complement_math' is negative
    7 |  typedef int compile_time_assert_## name [1 - 2*!(expr)]
      |              ^~~~~~~~~~~~~~~~~~~~
prog.c:9:1: note: in expansion of macro 'compile_time_assert'
    9 | compile_time_assert(twos_complement_math, INT_MIN == -INT_MIN);
      | ^~~~~~~~~~~~~~~~~~~

https://wandbox.org/permlink/yXGhc6fQ6mGs3fww

KennyMinigun ★★★★★
()
Ответ на: комментарий от KennyMinigun

потому что он константу как long int объявил. он выбрал минимальное необходимое для хранения константы значение в памяти.

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

Таки она права. 2147483648 — это long int.

Да, правда. Наверное поэтому в limits.h и написано (-INT_MAX - 1)

Но всё равно, по крайней мере текст ошибки неправильный.

error: variably modified 'compile_time_assert_twos_complement_math' at file scope

И тоже интересно, почему со _Static_assert прокатывает (да еще и компилируется!):

#include <limits.h>

_Static_assert(INT_MIN == -INT_MIN, "two's complement math");

int main() {}

prog.c:3:27: warning: integer overflow in expression '-2147483648' of type 'int' results in '-2147483648' [-Woverflow]
    3 | _Static_assert(INT_MIN == -INT_MIN, «two's complement math»);
      |     

https://wandbox.org/permlink/DhU5pJjxzu38hrxk

KennyMinigun ★★★★★
()
Ответ на: комментарий от SZT

Видел. Проблема в том, что example даётся, а определения integer overflow в документе нет.

Например, 6.3.1.3 чётко описывает, что происходит, если результат каста в signed не влазит в диапазон: either the result is implementation-defined or an implementation-defined signal is raised.

А для арифметики на signed типах нигде такого же правила не написано (я не нашел), кроме примера в словаре. Мде.

Это очень странно, почему каст оказался лучше специфицирован, чем арифметика.

Deleted
()
Ответ на: комментарий от Deleted

вот еще

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

Т.е. если у нас от INT_MIN отнимается 1, мы получаем «not in the range of representable values»

SZT ★★★★★
()

Как уже выше сказали, переполнение signed типов - UB. Не знаю точно, как в C, но в крестах одним из требований к constexpr-выражениям является отсутствие в них UB. Возможно, здесь что-то подобное

Deleted
()

Эта штука не даёт коду скомпилироваться, если выражение expr вычислилось в 0 или если оно не может быть вычислено на этапе компиляции.

Я не понял, какая вам разница, на каком этапе компиляции происходит проблема, если один из результатов — ошибка компиляции. Ладно бы вы препроцессором хотели обойти проблему, так ведь даже не стремились.

Делайте минимальные тестовые программки, как делает «configure» и на основе результатов (в том числе кросс-компилятором, но без вызова на выполнение) создаёте ifdef-ы.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Я не понял

Если вы чего-то не поняли, это повод задуматься и подтянуть знания, а не гордо нести своё непонимание в люди.

Deleted
()
Ответ на: комментарий от Deleted

Если вы чего-то не поняли, это повод задуматься и подтянуть знания, а не гордо нести своё непонимание в люди.

Я так и знал, что сейчас сыграет типичный лор: вникнуть в коммент не даёт возмущенное самомнение, как же — посмели в корне возразить, пусть и максимально вежливо с расшаркиванием. Ну так вот: не понял тут именно о вашем представлении о знаниях и методах их применения, а не о моих.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

Я так и знал, что сейчас сыграет типичный лор

Типичный лор сыграл в вас в тот момент, когда вы пришли в тему, посвященную арифметическому переполнению и начали учить, как правильно писать... а кстати, что именно писать? Какое отношение ваш комментарий имеет к теме топика - вот о чем вам стоило бы задуматься.

вникнуть в коммент

Да, вникнуть в пост было очень сложно.

Deleted
()
Ответ на: комментарий от Deleted

Какое отношение ваш комментарий имеет к теме топика - вот о чем вам стоило бы задуматься.

Если вы не заметили, я всегда делаю квотинг на который делаю коммент.

посвященную арифметическому переполнению

Угу. В препроцессоре. Который в #define работает со строками и потому ему до лампочки, что там в строке INT_MIN.

а кстати, что именно писать?

Я вам педложил не писать такие исходники, где заведомо предложено не компилироваться, но ещё и с возмущением, почему это не делает препроцессор.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Жжоте. Пришел, прочитал что-то своё, поучил жизни своё воображение, пошел дальше.

А с виду вполне адекватный юзер, судя по хоум пейджу. Атмосфера ЛОРа, не иначе.

Я вам педложил не писать такие исходники

Я вашего мнения не спрашивал, как мне писать исходники.

Deleted
()
Ответ на: комментарий от Deleted

Жжоте. Пришел, прочитал что-то своё, поучил жизни своё воображение, пошел дальше.

А вы что хотели? Топик помечен как решённый, про -INT_MIN вам уже всё рассказали. Вы хотели, чтобы тут и дальше над вами глумились? Если вы не поняли коммента на ваше же предложение, взятое из стартового сообщения — я не виноват.

Я вашего мнения не спрашивал, как мне писать исходники.

Тогда вы явно форум перепутали.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Тогда вы явно форум перепутали.

Приятно, что вы признали, что в вас сыграл типичный ЛОР.

Остался шаг два: прочитать ОП и перестать возражать воображению.

Вы хотели, чтобы тут и дальше над вами глумились?

Т.е. цель вашего комментария - поглумиться? А вы смельчак. Не всякий вот так открыто признается в таком мотиве.

Deleted
()
Ответ на: комментарий от Deleted

Остался шаг два: прочитать ОП и перестать возражать воображению.

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

Т.е. цель вашего комментария - поглумиться?

Это уже утомляет. Цель эта — у вас. Моя была понять, зачем вы так пишите. Но не судьба. Но, в прочем, не очень то и хотелось.

vodz ★★★★★
()
Ответ на: комментарий от vodz

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

Если к вам на улице подойдёт гопота с «дядя дай закурить», она тоже не просто так подойдёт, а среагировав на ваше невербальное сообщение в виде визуально считываемых тактико-технических характеристик. Вы себя сейчас ставите примерно в такое же положение - гопоты. Я с вами сейчас разговариваю исключительно в силу того, что посмотрел указанный вами возраст в хоумпейдже, и потому надеюсь, что вас отпустит - не школота же. Если бы не это, отправил бы уже в игнор давно.

Цель эта — у вас.

Пациент путается в показаниях. Добровольно пришел в топик и вступил в диалог, чтобы на ним глумились.

Моя была понять, зачем вы так пишите.

Ага, вот таким макаром:

Ладно бы вы препроцессором хотели обойти проблему, так ведь даже не стремились. Делайте минимальные тестовые программки, как делает «configure» и на основе результатов (в том числе кросс-компилятором, но без вызова на выполнение) создаёте ifdef-ы.

Объясняю.

Есть такая штука — assert(). Если не знаете, что это, ищите в гугле.

Иногда бывает нужно, чтобы assert() сработал в compile time и зарубил к чертовой матери сборку заведомо неисправной программы. Препроцессор для этого применим лишь очень ограниченно, поскольку не имеет представления о семантике программы. Поэтому до того, как в свежий си добавили _Static_assert(), приходилось делать трюк с typedef. Не знаю, кто этот трюк первый придумал, но это очень изобретательно!

Всё.

А ваши рассуждения и поучения тут вообще не в тему. Никакой задачи детектирования сборочного окружения assert не решает и решать не может.

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.