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

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:

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)

Wconversion

Testcases

   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" } */

   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 }

   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.

None: NewWconversion (last edited 2012-01-06 01:01:42 by JonathanWakely)