Consider these examples of implicit conversions in C:
void foo(unsigned int x)
{
int b = -5;
/* ... */
if (b < x) {
/*
* If b < 0, the check will always fail!
* b is first converted to unsigned int before comparison.
*/
}
}
/*
* If we first wrote the code using type int for coeff,
* but later refactored to use float as below,
* but forgot to update type of diff, we might get a rounding
* and possibly an undesired behavior.
*/
float coeff = /* ... compute some value ... */;
int diff = old_coeff - coeff;
You can ask the compiler to always warn on implicit conversions, so that user is expected to explicitly mark conversions and hopefully catch some mistakes.
With -Wconversion (-Wfloat-conversion, -Wsign-conversion, -Wsign-compare) you get:
a.c: In function ‘foo’:
a.c:7:11: warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
7 | if (b < x) {
| ^
a.c: In function ‘bar’:
a.c:26:16: warning: conversion from ‘float’ to ‘int’ may change value [-Wfloat-conversion]
26 | int diff = old_coeff - coeff;
| ^~~~~~~~~
a.c:26:9: warning: unused variable ‘diff’ [-Wunused-variable]
26 | int diff = old_coeff - coeff;
| ^~~~
As of day of writing this post, gcc and clang only catch -Wsign-compare with the usual "-Wall -Wextra".
Practical examples would be:
- tcp: Correct signedness in skb remaining space calculation
- xsk: fix OOB map writes when deleting elements
Automatic checking against runtime narrowing conversions
There is also an interesting talk Bjarne gave about concepts. He showed how you would use them to create a class wrapper in C++ to catch narrowing conversions at runtime. Arguably, using std::expected might be a better API for some use cases, but the idea is still general.