cgcc

Why am I getting "Assuming signed integer overflow does not occur" here?


Code in question (godbolt)

#include <stdbool.h>
void do_work(void);

typedef struct task {
    int timer;
    void (*job)(void);
} task_t;

typedef struct task_list {
    task_t *tasks;
    int length;
} task_list_t;

void test(task_list_t *task_list)
{
    for (int i = 0; i < task_list->length; i++)
    {
        task_t *task = &task_list->tasks[i];
        __attribute__((assume(task->timer > 0)));
        task->timer--;
        bool timer_finished = task->timer <= 0;
        if (timer_finished)
            task->job();
    }
}

I get the following output:

<source>: In function 'test':
<source>:25:1: warning: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C2 -+ C1 [-Wstrict-overflow]
   25 | }
      | ^
<source>:25:1: warning: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C2 -+ C1 [-Wstrict-overflow]
Compiler returned: 0

Let's say I can't change task_t::timer to an unsigned type. Why is the assume being ignored? Or am I fundamentally misunderstanding the warning?


Solution

  • The warning you're getting warning: assuming signed overflow does not occur [...] [-Wstrict-overflow] is due to the compiler letting you know it is assuming your integer variable task_t::timer won't ever be INT_MIN when decrementing (task->timer--;). Decrementing the variable when already at its minimum value would cause an overflow (or underflow) which the C standard defines as Undefined Behaviour (UB).

    The compiler assumes it won't overflow in order to optimize the resulting machine code. It's issuing a warning because, in optimizing the following:

    bool timer_finished = task->timer <= 0;
    

    it assumes that task->timer will not overflow. If it did, the comparison might behave unexpectedly due to UB.

    The __attribute__((assume)) helps the compiler understand certain properties at a specific point but it does not affect assumptions about integer overflows across the code. The compiler still respects the C standard, which allows it to optimize with the assumption that signed overflow is UB.

    To suppress the warning, you would need to make it clear to the compiler that the comparison will never be performed when a signed overflow is possible, e.g.:

    if (task->timer > 0) {
        task->timer--;
        bool timer_finished = task->timer <= 0;
        if (timer_finished)
            task->job();
    }
    

    This makes sure that task->timer only decrements when it’s positive, avoiding potential underflow issues and making the compiler happier and not spit out a warning :)