NOTE: This page is now obsolete and kept just for reference. It is completely superseded by NewWconversion.

Wcoercion option: warn of any implicit conversion that may change a value

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 coercions, that is, 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 in coercions where the value cannot change:

double d = sqrtf(2.0f);
size_t size = 20;
int i = 4.0;

Current situation

Wconversion:

Wconversion is 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. Wconversion is used to find where the use of protoize may introduce bugs in the code (see Protoize Caveats).

Thus, Wconversion is not recommended for general use. Although it may detect coercions that may change a value, such as:

void fint(int x);
void h {
  fint(3.2);
}

it will also generate warnings for perfectly reasonable code

void fchar(char x);
void h {
  fchar('A');
}

These warning messages are confusing for users, who are misled by the name of the option, when they use Wconversion for ISO C code 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 is exacerbated by the fact that Wconversion in addition warns for negative constants converted to unsigned type. Therefore, it is not surprising that many incorrectly believe that the purpose of 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 is intended for one-shot translations of old C code to ISO C, the fact that it warns for some coercions that may change a value is just a side-effect. Sadly, the wrong belief is currently wide-spread.

On the other hand, there are some warning options in gcc that identify coercions that may change a value, for example Wsign-compare.

Proposed solution

Our proposal is two warning options. One of them would encapsulate the behaviour required by protoize to detect where the addition of prototypes may led to new bugs. The other warning option will identify coercions that may change a value. Tentatively, we will call the former Wtraditional-conversion and the latter Wcoercion just for the sake of clarity and to distinguish both of them from the original Wconversion (we don't discard that the name Wconversion will be kept for one of them).

Wtraditional-conversion:

This definition of Wtraditional-conversion is actually the same as for Wconversion although we may decide to clarify it, warn that it is not intended for general use and point the reader to Wcoercion.

Wcoercion:

The definition of Wcoercion may also describe a number of selected examples suggestions are welcome.

Alternative names for Wtraditional-conversion might be Wprotoize, Wadded-prototypes and Wconversion. An alternative name for Wcoercion might be Wconversion.

Testcases

1. Negative integer constant expression is implicitly converted to an unsigned type. This is currently a feature of Wconversion which will be taken away by Wcoercion (see Bug 9072).

   1 /* Test for diagnostics for negative constants coerced to unsigned types
   2    These tests come from gcc/testsuite/gcc.dg/overflow-warn-2.c
   3    Origin: Manuel Lopez-Ibanez <lopezibanez@gmail.com> */
   4 /* { dg-do compile } */
   5 /* { dg-options "-std=c99 -Wcoercion" } */
   6 #include <limits.h>
   7 void fuc (unsigned char);
   8 void hc (int x)
   9 {
  10   unsigned char uc;
  11   fuc (-1); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  12   uc = -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  13   uc = x ? 1U : -1; /* { dg-warning "coercion to 'unsigned char' from 'unsigned int' may alter its value" } */
  14   /* { dg-warning "negative integer implicitly converted to unsigned type" "" { target *-*-* } 18 } */
  15   uc = x ? SCHAR_MIN : 1U; /* { dg-warning "coercion to 'unsigned char' from 'unsigned int' may alter its value" } */
  16   /* { dg-warning "negative integer implicitly converted to unsigned type" "" { target *-*-* } 20 } */
  17   uc = '\xa0'; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  18   fuc('A');
  19   uc = 'A';
  20   uc = x ? 1 : -1; /* { dg-warning "coercion to 'unsigned char' from 'int' may alter its value" } */
  21   uc = x ? SCHAR_MIN : 1; /* { dg-warning "coercion to 'unsigned char' from 'int' may alter its value" } */
  22 }
  23 unsigned fui (unsigned int ui);
  24 unsigned fsi (signed int ui);
  25 void hi (int x)
  26 {
  27   unsigned ui;
  28   fui (-1); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  29   ui = -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  30   ui = x ? 1U : -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  31   ui = x ? INT_MIN : 1U; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  32   ui = ui ? SCHAR_MIN : 1U; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  33   ui = 1U * -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  34   ui = ui + INT_MIN; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  35   ui = -1 * (1 * -1);
  36   ui = (unsigned) -1;
  37   ui = x ? 1 : -1; /* { dg-warning "coercion to 'unsigned int' from 'int' may alter its value" } */
  38   ui = x ? INT_MIN : 1; /* { dg-warning "coercion to 'unsigned int' from 'int' may alter its value" } */
  39   ui = ui ? SCHAR_MIN : 1; /* { dg-warning "coercion to 'unsigned int' from 'int' may alter its value" } */
  40 }
  41 void hs(void)
  42 {
  43   fsi(UINT_MAX - 1); /* { dg-warning "coercion as 'int' alters 'unsigned int' constant value" } */
  44   fsi(UINT_MAX - 1U); /* { dg-warning "coercion as 'int' alters 'unsigned int' constant value" } */
  45   fsi(UINT_MAX/3U);
  46   fsi(UINT_MAX/3);
  47   fui(UINT_MAX - 1);
  48 }
  49 unsigned fui(unsigned a) { return a + -1; } /* { dg-warning "negative integer implicitly converted to unsigned type" } */

2. Function calls Wconversion related, (see Bug 9072 comment 12). (The same test cases would apply to assignments)

   1 /* { dg-do compile }
   2 /* { dg-options "-fsigned-char -std=c99 -Wcoercion" } */
   3 #include <limits.h>
   4 #include <float.h>
   5 # define M_PI           3.14159265358979323846  /* pi */
   6 void fchar(char x);
   7 void ffloat(float x);
   8 void fdouble(double x);
   9 void flongdouble(long double x);
  10 void fshortint(short int x);
  11 void fint(int x);
  12 void flongint(long int x);
  13 void flonglong(long long x);
  14 void h(void)
  15 {
  16   unsigned int ui = 3;
  17   int   si = 3;
  18   float  f = 3;
  19   double d = 3;
  20   fint(3.1f); /* { dg-warning "coercion" } */
  21   fint(3.1); /* { dg-warning "coercion" } */
  22   ffloat(INT_MAX); /* { dg-warning "coercion" } */
  23   ffloat(DBL_MIN); /* { dg-warning "coercion" } */
  24   ffloat(M_PI); /* { dg-warning "coercion" } */
  25   ffloat(3.1); /* { dg-warning "coercion" } */
  26   fint(d); /* { dg-warning "coercion" } */
  27   ffloat(d); /* { dg-warning "coercion" } */
  28   ffloat(si); /* { dg-warning "coercion" } */
  29   ffloat(ui); /* { dg-warning "coercion" } */
  30   ffloat(16777217); /* { dg-warning "coercion" } */
  31   fint(3);
  32   fint(3.0f);
  33   fint(3.0);
  34   fint(16777217.0f);
  35   ffloat(3U);
  36   ffloat(3);
  37   ffloat(INT_MIN);
  38   fdouble(UINT_MAX);
  39   fdouble(ui);
  40   ffloat(3.0);
  41   ffloat(3.1f);
  42   ffloat(0.25);
  43   ffloat(f);
  44   fdouble(d);
  45   fdouble(f);
  46   fdouble(16777217);
  47 }
  48 void fuc(unsigned char uc);
  49 void fui(unsigned int  ui);
  50 void h2(void)
  51 {
  52   unsigned int ui = 3;
  53   int   si = 3;
  54   unsigned char uc = 3;
  55   signed char   sc = 3;
  56   fint(ui); /* { dg-warning "coercion" } */
  57   fui(si);/* { dg-warning "coercion" } */
  58   fui(sc); /* { dg-warning "coercion" } */
  59   fui(-1); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  60   fui('\xa0'); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  61   fuc('\xa0'); /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  62   fint(uc);
  63   fui(uc);
  64   fui('A');
  65   fuc('A');
  66   fint('A');
  67   fchar('A');
  68   fchar('\xa0');
  69 }

3. Assignments .

   1 /* { dg-do compile }
   2 /* { dg-options "-fsigned-char -std=c99 -Wcoercion" } */
   3 #include <limits.h>
   4 #include <float.h>
   5 # define M_PI           3.14159265358979323846  /* pi */
   6 float  vfloat;
   7 double vdouble;
   8 long double vlongdouble;
   9 char   vchar;
  10 short int vshortint;
  11 int vint;
  12 long vlongint;
  13 long long vlonglong;
  14 void h(void)
  15 {
  16   unsigned int ui = 3;
  17   int   si = 3;
  18   float  f = 3;
  19   double d = 3;
  20   vint = 3.1f; /* { dg-warning "coercion" } */
  21   vint = 3.1; /* { dg-warning "coercion" } */
  22   vfloat = INT_MAX; /* { dg-warning "coercion" } */
  23   vfloat = DBL_MIN; /* { dg-warning "coercion" } */
  24   vfloat = M_PI; /* { dg-warning "coercion" } */
  25   vfloat = 3.1; /* { dg-warning "coercion" } */
  26   vint = d; /* { dg-warning "coercion" } */
  27   vfloat = d; /* { dg-warning "coercion" } */
  28   vfloat = si; /* { dg-warning "coercion" } */
  29   vfloat = ui; /* { dg-warning "coercion" } */
  30   vfloat = 16777217; /* { dg-warning "coercion" } */
  31   /* No warning */
  32   vint = 3;
  33   vint = 3.0f;
  34   vint = 3.0;
  35   vint = 16777217.0f;
  36   vfloat = 3U;
  37   vfloat = 3;
  38   vfloat = INT_MIN;
  39   vdouble = UINT_MAX;
  40   vdouble = ui;
  41   vfloat = 3.0;
  42   vfloat = 3.1f;
  43   vfloat = 0.25;
  44   vfloat = f;
  45   vdouble = d;
  46   vdouble = f;
  47   vdouble = 16777217;
  48 }
  49 unsigned char vuc;
  50 unsigned int vui;
  51 void h2(void)
  52 {
  53   unsigned int ui = 3;
  54   int   si = 3;
  55   unsigned char uc = 3;
  56   signed char   sc = 3;
  57   vint = ui;/* { dg-warning "coercion" } */
  58   vui = si; /* { dg-warning "coercion" } */
  59   vui = sc; /* { dg-warning "coercion" } */
  60   vui = -1; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  61   vui = '\xa0'; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  62   vuc = '\xa0'; /* { dg-warning "negative integer implicitly converted to unsigned type" } */
  63   /* no warning */
  64   vint = uc;
  65   vui = uc;
  66   vui = 'A';
  67   vuc = 'A';
  68   vint = 'A';
  69   vchar = 'A';
  70   vchar = '\xa0';
  71 }

4. Decimal float .

   1 /* { dg-do compile } */
   2 /* { dg-options "-std=gnu99 -Wcoercion" } */
   3 void fd32(_Decimal32);
   4 void fd64(_Decimal64);
   5 void fd128(_Decimal128);
   6 void fint (int);
   7 _Decimal32 d32;
   8 _Decimal64 d64;
   9 _Decimal128 d128;
  10 float vfloat;
  11 double vdouble;
  12 long double vlongdouble;
  13 void h()
  14 {
  15   vfloat = 2.0df;
  16   vdouble = 2.0df;
  17   vlongdouble = 2.0df;
  18   vfloat = -7.0dd;
  19   vdouble = -7.0dd;
  20   vlongdouble = -7.0dd;
  21   vfloat = 30.0dl;
  22   vdouble = 30.0dl;
  23   vlongdouble = 30.0dl;
  24   d32 = 3;
  25   d64 = 3;
  26   d128 = 3;
  27   d32 = -2.0;
  28   d64 = -2.0;
  29   d128 = -2.0;
  30   fd128(d128);
  31   fd128(d64);
  32   fd128(d32);
  33   fd64(d64);
  34   fd64(d32);
  35   fd32(d32);
  36   fint(2.0df);
  37   fint(2.0dd);
  38   fint(2.0dl);
  39   d64 = 123.4df;
  40   d128 = 123.4df;
  41   d128 = 345.678dd;
  42   /* Conversions to smaller types for which the value fits.  */
  43   d32 = 3456.789dd;
  44   d32 = 123.4567dl;
  45   d64 = 1234567890.123456dl;
  46 }
  47 void g()
  48 {
  49   vfloat = 2.1df; /* { dg-warning "coercion as 'float' alters '_Decimal32' constant value" } */
  50   vdouble = 2.1df; /* { dg-warning "coercion as 'double' alters '_Decimal32' constant value" } */
  51   vlongdouble = 2.1df; /* { dg-warning "coercion as 'long double' alters '_Decimal32' constant value" } */
  52   fint(2.1df); /* { dg-warning "coercion as 'int' alters '_Decimal32' constant value" } */
  53   fint(2.1dd); /* { dg-warning "coercion as 'int' alters '_Decimal64' constant value" } */
  54   fint(2.1dl); /* { dg-warning "coercion as 'int' alters '_Decimal128' constant value" } */
  55   d32 = 9.99999949E96DD; /* { dg-warning "coercion as '_Decimal32' alters '_Decimal64' constant value" } */
  56   d64 = 9.9999999999999995E384DL;  /* { dg-warning "coercion as '_Decimal64' alters '_Decimal128' constant value" } */
  57   fd32(d128); /* { dg-warning "coercion to '_Decimal32' from '_Decimal128' may alter its value" } */
  58   fd32(d64); /* { dg-warning "coercion to '_Decimal32' from '_Decimal64' may alter its value" } */
  59   fd64(d128); /* { dg-warning "coercion to '_Decimal64' from '_Decimal128' may alter its value" } */
  60   d32 = vfloat; /* { dg-warning "coercion to '_Decimal32' from 'float' may alter its value" } */
  61   d32 = vdouble; /* { dg-warning "coercion to '_Decimal32' from 'double' may alter its value" } */
  62   d64 = vdouble; /* { dg-warning "coercion to '_Decimal64' from 'double' may alter its value" } */
  63   d128 = vdouble; /* { dg-warning "coercion to '_Decimal128' from 'double' may alter its value" } */
  64   vfloat  = d32; /* { dg-warning "coercion to 'float' from '_Decimal32' may alter its value" } */
  65   vdouble = d32; /* { dg-warning "coercion to 'double' from '_Decimal32' may alter its value" } */
  66   vlongdouble = d32; /* { dg-warning "coercion to 'long double' from '_Decimal32' may alter its value" } */
  67 }

Oustanding Issues

int i = 16777217.0f; /*  i == 16777216 */

float f = 16777217.0f; /*  f == 16777216 */

   1   double d = 0.3;
   2   float  f;
   3   f = d;    /* Warning (double) to (float) !  */
   4   d = 3;    /* No warning                     */
   5   f = d;    /* Warning because there is no flow control in front ends
   6               (so we don't know the value of d) */

   1 void k(int) {}
   2 int main()
   3 {
   4   int i = NULL; // { dg-warning "" } converting NULL to non-pointer type
   5   float z = NULL; // { dg-warning "" } converting NULL to non-pointer type
   6   int a[2];
   7   i != NULL; // { dg-warning "" } NULL used in arithmetic
   8   NULL != z; // { dg-warning "" } NULL used in arithmetic
   9   k != NULL; // No warning: decay conversion
  10   NULL != a; // Likewise.
  11   -NULL;     // { dg-warning "" } converting NULL to non-pointer type
  12   +NULL;     // { dg-warning "" } converting NULL to non-pointer type
  13   ~NULL;     // { dg-warning "" } converting NULL to non-pointer type
  14   a[NULL] = 3; // { dg-warning "" } converting NULL to non-pointer-type
  15   i = NULL;  // { dg-warning "" } converting NULL to non-pointer type
  16   z = NULL;  // { dg-warning "" } converting NULL to non-pointer type
  17   k(NULL);   // { dg-warning "" } converting NULL to int
  18   NULL && NULL; // No warning: converting NULL to bool is OK
  19 }

   1   #include <limits.h>
   2   void h(unsigned int ui, signed int si)
   3   {
   4       si = (si > ui) ? INT_MAX : INT_MIN; /* {dg-warning "comparison between signed and unsigned"} */
   5   }
   6   void h2(void)
   7   {
   8       int i;
   9       for(i=0; i < sizeof(double); i++); /* {dg-warning "comparison between signed and unsigned"} */
  10   }

   1 #include <string.h>
   2 
   3 int main (int argc, char *argv[])
   4 {
   5     char *s1 = argv[0];
   6     char *s2 = strchr (s1, '/');
   7     char r;
   8 
   9     (void) argc;
  10 
  11     r = s2 ? (s2 - s1) : strlen (s1);
  12     return 0;
  13 }

( maybe or maybe not? ) Complete list

Acknowledgements

First-time GCC development

Get the code

(1)

What directories are not required to build and test gcc and g++ ?

Removing directories from your subversion working copy (2) : there is little point to do this since you need all of them to perform a complete bootstrap and test.

Build

(3)

cd /home/manu/myproject
mkdir -p objdir local
cd objdir 

Configure: just need to do it once unless you want to change configuration or paths or do a complete bootstrap and test

./src/trunk/configure --prefix=`pwd`/../local --enable-languages=c,c++,objc --disable-mudflap --disable-libmudflap 

Build:

make -j 2 all-gcc #  this still takes an awful lot of time
make install      #  is this actually needed ?  

Interesting options for configure and make ?

Testing

(4, 5, 6)

cd objdir
make -C gcc check-gcc RUNTESTFLAGS=dg.exp # all tests of dg
make -C gcc check-gcc RUNTESTFLAGS=dg.exp=overflow-warn-1.c # to test only overflow-warn-1.c 

Any changes, except changes in front ends different from the C one, require a complete bootstrap and test: (5)

cd objdir
./src/trunk/configure --prefix=`pwd`/../local --enable-languages=all
make bootstrap
make -k check 

make check-gcc check-c++ check-objc check-obj-c++  # it may take several hours... 

Produce a patch

 diff -up trunk/ wcoercion/ > woercion.patch 
 svn diff --diff-cmd diff -x -up trunk/ wcoercion/ > woercion.patch 

Other proposals

I think there are two different types of conversions:

Is this checking done by the middle end or by C/C++ front end? Especially, if the former, it would be nice if it were working with Fortran as well.--TobiasBurnus

None: Wcoercion (last edited 2008-01-10 19:38:58 by localhost)