Home > Enterprise >  -Wconversion diagnostic from gcc-trunk when -fsanitize=undefined is passed
-Wconversion diagnostic from gcc-trunk when -fsanitize=undefined is passed

Time:02-20

This is about the correct diagnostics when short ints get promoted during "usual arithmetic conversions". During operation / a diagnostic could be reasonably emitted, but during /= none should be emitted.

Behaviour for gcc-trunk and clang-trunk seems OK (neither emits diagnostic for first or second case below)... until...

we add the entirely unrelated -fsanitize=undefined ... after which, completely bizarrely:

gcc-trunk emits a diagnostic for both cases. It really shouldn't for the 2nd case, at least.

Is this a bug in gcc?

Godbolt link

Godbolt link with -O3 - same result

int main() {
    short sum   = 50;
    short count = 10;

    // sum and count get promoted to int for the "usual arithmetic conversions"
    // then the assignment could result in a reasonable -Wconversion diagnostic for reduction back
    // to short
    // However clang-trunk and gcc-trunk choose NOT TO issue a diagnostic with -Wconversion enabled
    short avg1 = sum / count;

    // we should be able to prevent promotion to int by using /= assignment operator.
    // Both clang-trunk and gcc-trunk, correctly, DON'T issue a diagnostic with -Wconversion enabled
    auto tmp = sum;
    tmp /= count;
    short avg2 = tmp;

    // HOWEVER if we add -fsanitize=undefined for both compilers
    // then, bizarrly, gcc-trunk issues a diagnostic for both cases above and clang-trunk still for
    // neither

    // none of these ever issue a diagnostic (nor should they)
    tmp  = count; // all
    tmp -= count; // are
    tmp *= count; // silent

    return (avg1   avg2) & 0xff; // prevent "unused" diagnostics
}

CodePudding user response:

For a built-in compound assignment operator $= the expression A $= B behaves identical to an expression A = A $ B, except that A is evaluated only once. All promotions and other usual arithmetic conversions and converting back to the original type still happen.

Therefore it shouldn't be expected that the warnings differ between short avg1 = sum / count; and tmp /= count;.

A conversion from int to short happens in each case. So a conversion warning would be appropriate in either case.

However, the documentation of GCC warning flags says specifically that conversions back from arithmetic on small types which are promoted is excluded from the -Wconversion flag. GCC offers the -Warith-conversion flag to include such cases nonetheless. With it all arithmetic in your examples generates a warning.

Also note that this exception to -Wconversion has been introduced only with GCC 10. For some more context on it, the bug report from which it was introduced is here.

It seems that Clang has always been more lenient on these cases than GCC. See for example this issue and this issue.


For / in GCC -fsanitize=undefined seems to break the exception that -Wconversion is supposed to have. It seems to me that this is related to the undefined behavior sanitizer adding a null-value check specifically for division. Maybe, after this transformation, the warning flag logic doesn't recognize it as direct arithmetic on the smaller type anymore.

If my understanding of the intended behavior of the warning flags is correct, I would say that this looks unintended and thus is a bug.

CodePudding user response:

supplementary information for discussion under answer by user17732522.

The code below appears to illustrate a case where using = (with single small integer RHS ONLY!) vs = with 2 term binary small integer operation RHS vs simple = does help?

gcc emits a -Wconversion diagnostic for the first and third case. Clang emits none.

Why is that? Is this "another inconsistency" in the way gcc emits these diagnostics? Or is the change implemented as a result of this bug covering just the narrow case of compound assignment with a single term small integer RHS ?

Note: this is all without -fsanitizer

Updated Godbolt link

int main() {
    short a   = 10;
    short b = 20;

    short sum1 = 30;
    sum1  = b - a;

    short sum2 = 30;
    sum2  = b;
    sum2 -= a;

    short sum3 = 30   b - a;

    return (sum1   sum2   sum3) & 0xff; // prevent "unused" diagnostics
}
  • Related