The new Wconversion option
Google Summer of Code 2006 Project
Student: Manuel López-Ibáñez (Email: manu gcc gnu org)
Mentor: Ian Lance Taylor
Synopsis
Starting from GCC version 4.3, the behaviour of the option Wconversion has changed. Its original purpose was helping to translate old C code to modern C standards by identifying the places where adding function prototypes may result in different behaviour. The new Wconversion option warns for any implicit conversion that may change a value. Examples of such conversions are passing a double value to a function declared to receive a float argument and assigning a signed constant to an unsigned variable. The option should not warn for explicit conversions or for cases where the value cannot in fact change despite the implicit conversion.
Background
We are interested on implicit type conversions that may alter a value. Such alteration can be overflow, underflow, sign change, truncation and loss of precision.
float f = sqrt(2.0);
int si = 0.5;
unsigned int ui = -1;
We are not interested on conversions where the value cannot in fact change:
double d = sqrtf(2.0f);
size_t size = 20;
int i = 4.0;
Situation previous to GCC 4.3
Wconversion:
Warn if a prototype causes a type conversion that is different from what would happen to the same argument in the absence of a prototype. This includes conversions of fixed point to floating and vice versa, and conversions changing the width or signedness of a fixed point argument except when the same as the default promotion.
Also, warn if a negative integer constant expression is implicitly converted to an unsigned type. For example, warn about the assignment x = -1 if x is unsigned. But do not warn about explicit casts like (unsigned) -1.
The original Wconversion was solely intended to translate old C code (like K&R code) to ISO C [ debian-gcc 2003 ]. In particular, GCC provides a tool, protoize, to add prototypes to old C code, which is a requisite to convert a program to ISO C. The purpose of the original Wconversion was to find where the use of protoize may introduce bugs in the code (see Protoize Caveats).
Thus, Wconversion was not recommended for general use. Although the original Wconversion was able to detect conversions that may change a value, such as:
void fint(int x);
void h(void)
{
fint(3.2); /* warning: passing argument 1 of ‘fint’ as integer rather than floating due to prototype */
}
it also generated warnings for perfectly reasonable code
void fshort(short x);
void h(short y)
{
fshort(y); /* warning: passing argument 1 of ‘fshort’ with different width due to prototype */
}
The warning messages confused users, who were misled by the name of the option [ gnu.gcc.bug 1999, GNU libc FAQ, gcc 2000, gcc 2001, gcc 2002, freebsd-questions 2003, tcl-bugs 2003, gsl-discuss 2005, comp.lang.c 2005 ].
This confusion was exacerbated by the fact that the original Wconversion also warned for negative constants converted to unsigned type. Therefore, it is not surprising that many incorrectly believe that the purpose of the original Wconversion is to warn about implicit type conversions that could cause unexpected results [ An Introduction to GCC, Network Theory Ltd ], when actually it is not. It was intended for one-shot translations of old C code to ISO C, the fact that it warns for some conversions that may change a value is just a side-effect. Sadly, the wrong belief is currently wide-spread.
Situation since GCC 4.3
The original Wconversion has been divided into two options, Wtraditional-conversion and a new and different Wconversion option. The first of them, Wtraditional-conversion, encapsulates the behaviour required by protoize to detect where the addition of prototypes may lead to new bugs. The new Wconversion identifies implicit conversions that may change a value. A sub-option Wsign-conversion gives finer control over conversions between signed and unsigned types.
Wtraditional-conversion (C only)
- Warn if a prototype causes a type conversion that is different from what would happen to the same argument in the absence of a prototype. This includes conversions of fixed point to floating and vice versa, and conversions changing the width or signedness of a fixed point argument except when the same as the default promotion.
Wconversion
Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion. For C++, also warn for conversions between NULL and non-pointer types; confusing overload resolution for user-defined conversions; and conversions that will never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.
Testcases
Conversions between integer types: testsuite/gcc.dg/Wconversion-integer.c and testsuite/gcc.dg/Wconversion-integer-no-sign.c (similar to testsuite/g++.dg/warn/Wconversion-integer.C and testsuite/g++.dg/warn/Wsign-conversion.C)
1 /* Test for diagnostics for implicit conversions between integer types
2 These tests come from testsuite/gcc.dg/Wconversion-integer.c */
3
4 /* { dg-do compile } */
5 /* { dg-options "-std=c99 -fsigned-char -Wconversion" } */
6
7 #include <limits.h>
8
9 void fsc (signed char sc);
10 void fuc (unsigned char uc);
11 unsigned fui (unsigned int ui);
12 void fsi (signed int ui);
13
14 void h (int x)
15 {
16 unsigned int ui = 3;
17 int si = 3;
18 unsigned char uc = 3;
19 signed char sc = 3;
20
21 uc = ui; /* { dg-warning "conversion" } */
22 uc = si; /* { dg-warning "conversion" } */
23 sc = ui; /* { dg-warning "conversion" } */
24 sc = si; /* { dg-warning "conversion" } */
25 fuc (ui); /* { dg-warning "conversion" } */
26 fuc (si); /* { dg-warning "conversion" } */
27 fsc (ui); /* { dg-warning "conversion" } */
28 fsc (si); /* { dg-warning "conversion" } */
29
30 fsi (si);
31 fui (ui);
32 fsi (uc);
33 si = uc;
34 fui (uc);
35 ui = uc;
36 fui ('A');
37 ui = 'A';
38 fsi ('A');
39 si = 'A';
40 fuc ('A');
41 uc = 'A';
42
43 uc = x ? 1U : -1; /* { dg-warning "conversion" } */
44 /* { dg-warning "negative integer implicitly converted to unsigned type" "" { target *-*-* } 43 } */
45 uc = x ? SCHAR_MIN : 1U; /* { dg-warning "conversion" } */
46 /* { dg-warning "negative integer implicitly converted to unsigned type" "" { target *-*-* } 45 } */
47 uc = x ? 1 : -1; /* { dg-warning "conversion" } */
48 uc = x ? SCHAR_MIN : 1; /* { dg-warning "conversion" } */
49 ui = x ? 1U : -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
50 ui = x ? INT_MIN : 1U; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
51 ui = ui ? SCHAR_MIN : 1U; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
52 ui = 1U * -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
53 ui = ui + INT_MIN; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
54 ui = x ? 1 : -1; /* { dg-warning "conversion" } */
55 ui = ui ? SCHAR_MIN : 1; /* { dg-warning "conversion" } */
56
57 fuc (-1); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
58 uc = -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
59 fui (-1); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
60 ui = -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
61 fuc ('\xa0'); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
62 uc = '\xa0'; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
63 fui ('\xa0');/* { dg-warning "negative integer implicitly converted to unsigned type" } */
64 ui = '\xa0'; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
65 fsi (0x80000000); /* { dg-warning "conversion" } */
66 si = 0x80000000; /* { dg-warning "conversion" } */
67
68
69 fsi (UINT_MAX - 1); /* { dg-warning "conversion" } */
70 si = UINT_MAX - 1; /* { dg-warning "conversion" } */
71 fsi (UINT_MAX - 1U); /* { dg-warning "conversion" } */
72 si = UINT_MAX - 1U; /* { dg-warning "conversion" } */
73 fsi (UINT_MAX/3U);
74 si = UINT_MAX/3U;
75 fsi (UINT_MAX/3);
76 si = UINT_MAX/3;
77 fui (UINT_MAX - 1);
78 ui = UINT_MAX - 1;
79
80 uc = (unsigned char) -1;
81 ui = -1 * (1 * -1);
82 ui = (unsigned) -1;
83
84 fsc (uc); /* { dg-warning "conversion" } */
85 sc = uc; /* { dg-warning "conversion" } */
86 fuc (sc); /* { dg-warning "conversion" } */
87 uc = sc; /* { dg-warning "conversion" } */
88 fsi (ui); /* { dg-warning "conversion" } */
89 si = ui; /* { dg-warning "conversion" } */
90 fui (si); /* { dg-warning "conversion" } */
91 ui = si; /* { dg-warning "conversion" } */
92 fui (sc); /* { dg-warning "conversion" } */
93 ui = sc; /* { dg-warning "conversion" } */
94 }
95
96 unsigned fui (unsigned a) { return a + -1; } /* { dg-warning "negative integer implicitly converted to unsigned type" } */
Conversions between floating-point types: testsuite/gcc.dg/Wconversion-real.c (duplicated as testsuite/g++.dg/warn/Wconversion-real.C)
1 /* Test for diagnostics for Wconversion for floating-point (testsuite/gcc.dg/Wconversion-real.c) */
2
3 /* { dg-do compile }
4 /* { dg-options "-std=c99 -Wconversion" } */
5
6 float vfloat;
7 double vdouble;
8 long double vlongdouble;
9
10 void ffloat (float f);
11 void fdouble (double d);
12 void flongdouble (long double ld);
13
14 void h (void)
15 {
16 float f = 0;
17 double d = 0;
18 long double ld = 0;
19
20 ffloat (3.1); /* { dg-warning "conversion" } */
21 vfloat = 3.1; /* { dg-warning "conversion" } */
22 ffloat (3.1L); /* { dg-warning "conversion" } */
23 vfloat = 3.1L; /* { dg-warning "conversion" } */
24 fdouble (3.1L); /* { dg-warning "conversion" "" { target large_long_double } } */
25 vdouble = 3.1L; /* { dg-warning "conversion" "" { target large_long_double } } */
26 ffloat (vdouble); /* { dg-warning "conversion" } */
27 vfloat = vdouble; /* { dg-warning "conversion" } */
28 ffloat (vlongdouble); /* { dg-warning "conversion" } */
29 vfloat = vlongdouble; /* { dg-warning "conversion" } */
30 fdouble (vlongdouble); /* { dg-warning "conversion" "" { target large_long_double } } */
31 vdouble = vlongdouble; /* { dg-warning "conversion" "" { target large_long_double } } */
32
33
34 ffloat ((float) 3.1);
35 vfloat = (float) 3.1;
36 ffloat ((float) 3.1L);
37 vfloat = (float) 3.1L;
38 fdouble ((double) 3.1L);
39 vdouble = (double) 3.1L;
40 ffloat ((float) vdouble);
41 vfloat = (float) vdouble;
42 ffloat ((float) vlongdouble);
43 vfloat = (float) vlongdouble;
44 fdouble ((double) vlongdouble);
45 vdouble = (double) vlongdouble;
46
47
48 ffloat (3.0);
49 vfloat = 3.0;
50 ffloat (3.1f);
51 vfloat = 3.1f;
52 ffloat (0.25L);
53 vfloat = 0.25L;
54
55
56 fdouble (3.0);
57 vdouble = 3.0;
58 fdouble (3.1f);
59 vdouble = 3.1f;
60 fdouble (0.25L);
61 vdouble = 0.25L;
62
63 flongdouble (3.0);
64 vlongdouble = 3.0;
65 flongdouble (3.1f);
66 vlongdouble = 3.1f;
67 flongdouble (0.25L);
68 vlongdouble = 0.25L;
69
70 ffloat (f);
71 vfloat = f;
72 fdouble (f);
73 vdouble = f;
74 fdouble (d);
75 vdouble = d;
76 flongdouble (f);
77 vlongdouble = f;
78 flongdouble (d);
79 vlongdouble = d;
80 flongdouble (ld);
81 vlongdouble = ld;
82 }
Conversions between floating-point and integer types: testsuite/gcc.dg/Wconversion-real-integer.c (duplicated as testsuite/g++.dg/warn/Wconversion-real-integer.C)
1 /* Test for diagnostics for Wconversion between floating-point and
2 integers (testsuite/gcc.dg/Wconversion-real-integer.c). */
3
4 /* { dg-do compile }
5 /* { dg-options "-std=c99 -Wconversion" } */
6
7 #include <limits.h>
8
9 void fsi (signed int x);
10 void fui (unsigned int x);
11 void ffloat (float x);
12 void fdouble (double x);
13
14 float vfloat;
15 double vdouble;
16
17 void h (void)
18 {
19 unsigned int ui = 3;
20 int si = 3;
21 unsigned char uc = 3;
22 signed char sc = 3;
23 float f = 3;
24 double d = 3;
25
26 fsi (3.1f); /* { dg-warning "conversion" } */
27 si = 3.1f; /* { dg-warning "conversion" } */
28 fsi (3.1); /* { dg-warning "conversion" } */
29 si = 3.1; /* { dg-warning "conversion" } */
30 fsi (d); /* { dg-warning "conversion" } */
31 si = d; /* { dg-warning "conversion" } */
32 fui (-1.0); /* { dg-warning "overflow" } */
33 ui = -1.0; /* { dg-warning "overflow" } */
34 ffloat (INT_MAX); /* { dg-warning "conversion" } */
35 vfloat = INT_MAX; /* { dg-warning "conversion" } */
36 ffloat (16777217); /* { dg-warning "conversion" } */
37 vfloat = 16777217; /* { dg-warning "conversion" } */
38 ffloat (si); /* { dg-warning "conversion" } */
39 vfloat = si; /* { dg-warning "conversion" } */
40 ffloat (ui); /* { dg-warning "conversion" } */
41 vfloat = ui; /* { dg-warning "conversion" } */
42
43 fsi (3);
44 si = 3;
45 fsi (3.0f);
46 si = 3.0f;
47 fsi (3.0);
48 si = 3.0;
49 fsi (16777217.0f);
50 si = 16777217.0f;
51 fsi ((int) 3.1);
52 si = (int) 3.1;
53 ffloat (3U);
54 vfloat = 3U;
55 ffloat (3);
56 vfloat = 3;
57 ffloat (INT_MIN);
58 vfloat = INT_MIN;
59 ffloat (uc);
60 vfloat = uc;
61 ffloat (sc);
62 vfloat = sc;
63
64 fdouble (UINT_MAX);
65 vdouble = UINT_MAX;
66 fdouble (ui);
67 vdouble = ui;
68 fdouble (si);
69 vdouble = si;
70 }
Frequently Asked Questions
Why isn't Wconversion enabled by -Wall or at least by -Wextra?
Implicit conversions are very common in C. This tied with the fact that there is no data-flow in front-ends (see next question) results in hard to avoid warnings for perfectly working and valid code. Wconversion is designed for a niche of uses (security audits, porting 32 bit code to 64 bit, etc.) where the programmer is willing to accept and workaround invalid warnings. Therefore, it shouldn't be enabled if it is not explicitly requested.
Why does Wconversion emit a warning in an implicit conversion between variables even when it is known at compile time that the value does not change?
double d = 1.0;
int i;
i = d; /* Warning because there is no flow control in front-ends
(so we don't know the value of d). */
Unfortunately, there is no data-flow solving in front-ends. That is, even if the value of a variable is known at compile time, it is not known by the front-end.
Why doesn't Wconversion work for FORTRAN, Java, Ada, etc. ?
The Wconversion option, as most warning options, is implemented in the front-end. Currently, it is only implemented for C/C++. Moreover, each language has different implicit type conversions and each corresponding front-end has a different design, thus each of them would require a different implementation.
Why doesn't Wconversion warn for unary minus applied to unsigned types ?
unsigned h (unsigned i) {
return -i;
}
Unary minus applied to unsigned types has an established meaning.
Why doesn't Wconversion work with decimal floating point?
There are patches to implement decimal floating point support for Wconversion, I tried getting them reviewed without success. Please, feel free to ping the relevant maintainers.
Why does GCC warn for int i = -2147483648?
-2147483648 in C/C++ is the integer constant 2147483648 (which doesn't fit into int), which is afterwards negated, you should write -2147483647-1, and you should be using 4294967295U if you want an unsigned int constant.