当传递 -fsanitize=undefined 时,来自 gcc-trunk 的 -Wconversion 诊断

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

这是关于 short ints 在“常规算术转换”期间得到提升时的正确诊断。在操作期间 / 可以合理地发出诊断,但在 /= 期间应该发出 none。

gcc-trunk 和 clang-trunk 的行为似乎没问题(下面的第一种或第二种情况都没有发出诊断)...直到...

我们添加完全不相关的 -fsanitize=undefined ... 之后,完全奇怪:

gcc-trunk 为这两种情况发出诊断。 至少第二种情况确实不应该。

这是 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
}

编辑

这是我为 GCC 提交的错误: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104616

对于 built-in 复合赋值运算符 $=,表达式 A $= B 的行为与表达式 A = A $ B 相同,只是 A 仅计算一次。所有提升和其他常见的算术转换以及转换回原始类型仍然会发生。

因此,不应期望 short avg1 = sum / count;tmp /= count; 之间的警告不同。

在每种情况下都会发生从 intshort 的转换。因此,无论哪种情况,转换警告都是合适的。

但是,the documentation of GCC warning flags 特别指出,从 -Wconversion 标志中排除了对提升的小型类型的算术转换。尽管如此,GCC 还是提供了 -Warith-conversion 标志来包含此类情况。使用它 all 算术在你的例子中产生一个警告。

另请注意,-Wconversion 的这个例外仅在 GCC 10 中引入。有关它的更多上下文,引入它的错误报告是 here

似乎Clang在这些情况下一直比GCC宽容。例如参见 [​​=26=].


对于 GCC 中的 / -fsanitize=undefined 似乎打破了 -Wconversion 应该有的异常。在我看来,这与未定义的行为消毒剂有关,它专门为除法添加了 null-value 检查。也许,在这个转换之后,警告标志逻辑不再将其识别为较小类型的直接算术。

如果我对警告标志的预期行为的理解是正确的,我会说这看起来不是预期的,因此是一个错误。

用户17732522回答下讨论的补充信息。

下面的代码似乎说明了使用 +=(仅使用单个小整数 RHS!)与 += 使用 2 项二进制小整数运算 RHS 与简单 = 的情况有帮助吗?

gcc 为第一种和第三种情况发出 -Wconversion 诊断。 Clang 发出 none.

这是为什么?这是 gcc 发出这些诊断的方式中的“另一个不一致”吗?还是由于 this bug 仅涵盖使用单个小整数 RHS 的复合赋值的狭义情况而实施的更改?

注意:这都是没有-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
}