0

Declarations:

/* Olson and Salop */
double os(double,double,short int);

/* Olson, Salop, and Taulberg */
double ost(double,double,short int,int,int);

Code snippets:

/* Olson and Salop */

double os(double rx,double ita,short int charge)
{
    double a=0.0;
    double b=0.0;

    a=18.26/sqrt((double)charge);
    b=1.872*sqrt(ita/(double)charge);

    return a*exp(-b*rx);
}

/* Olson, Salop, and Taulberg */

double ost(double rx,double ita,short int charge,int PQN,int AQN)
{
    /* AQN - azimuthal quantum number */
    /* PQN - principle quantum number */
    return os(rx,ita,charge)*exp(0.5*(log(2.0*AQN+1.0)-lgamma((double)PQN-AQN)-lgamma((double)(PQN+AQN+1)))+lgamma((double)PQN)); /* line 64 */
}

Compiler flags:

CC=gcc
WFLAGS=-W \
       -Wall \
       -Werror \
       -Wshadow \
       -Wcast-qual \
       -Wcast-align \
       -Wconversion \
       -Wwrite-strings \
       -Wpointer-arith \
       -Wnested-externs \
       -Wstrict-prototypes \
       -Wmissing-prototypes
CFLAGS=$(WFLAGS) \
       -g \
       -O2 \
       -ansi \
       -pedantic \
       -Dinline= \
       -fno-common \
       -fshort-enums
       -fno-common \
       -fshort-enums

Compiler:

$ gcc --version i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Compiler warning/error:

$ make gcc -W -Wall -Werror -Wshadow -Wcast-qual -Wcast-align -Wconversion -Wwrite-strings -Wpointer-arith -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -g -O2 -ansi -pedantic -Dinline= -fno-common -fshort-enums -c couplings.c cc1: warnings being treated as errors couplings.c: In function 'ost': couplings.c:64: warning: passing argument 3 of 'os' with different width due to prototype make: *** [couplings.o] Error 1

Why is this warning occurring? Everything for argument 3 of os() is declared as short int. I know I can get rid of -Wconversion to prevent the warning, but I'd rather address the real cause. And no, this is not homework.

Thanks.

Mannix
  • 411
  • 10
  • 23
  • 1
    For what it's worth, I cannot reproduce this warning/error using clang and the same command-line options, and I don't see anything that would cause it from what you've posted. Could you drop this all into a single compilable file with the minimal amount of code that'll reproduce the problem, and post it, just to make sure there's nothing else weird in there? – Crowman Apr 29 '14 at 17:21
  • Looks like a bogus warning to me. – M.M Apr 30 '14 at 03:00

2 Answers2

4

As the -Wconversion flag warns, you are experiencing an implicit type conversion. In this case charge is being converted to an int in ost (it's an expression). and being truncated to a short for the function call.

Here's a short summary of things you can actually do to woraround/fix the error, since with -Werror this will break your build. I'm assuming that the GCC 4.2.1 compiler that's causing the error is the only GCC compiler you are using that's earlier than GCC 4.3.0, since that's when the meaning of -Wconversion changed.

  1. Copy charge to a temporary variable in ost
    • (-) source change that's a kluge for the particular compiler, but shouldn't break the API
    • (+) no build changes
    • (-) what -Wconversion means still varies
  2. Change charge in os to be int
    • (-) source change which could break the API (if os is really extern)
    • (+) no build changes
    • (-) what -Wconversion means still varies
  3. Change charge in os to be double
    • pros/cons as above
  4. Remove -Wconversion from the problem build
    • (+) no source changes
    • (-) build changes
    • (+) fixes -Wconversion
  5. Upgrade the problem compiler to GCC >= 4.3.0
    • (+) no source changes
    • (+) no build changes
    • (+) fixes -Wconversion
    • (-) attendant problems when switching toolchains on a platform

EDIT:

Reproduced on CentOS 4.9 (x86):

$ gcc -W -Wall -Werror -Wshadow -Wcast-qual -Wcast-align \
-Wconversion -Wwrite-strings -Wpointer-arith -Wnested-externs \
-Wstrict-prototypes -Wmissing-prototypes \
-g -O2 -ansi -pedantic -Dinline= -D_XOPEN_SOURCE \
-fno-common -fshort-enums -c foo.c
foo.c: In function `ost':
foo.c:23: warning: passing arg 3 of `os' with different width due to prototype
make: *** [foo.o] Error 1
$ gcc --version
gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Another quite old compiler. FWIW, I get the error with casting (short int)charge as a parameter and introducing an intermediate variable short int Z=charge; return os(rx,ita,Z)...

I tried the int workaround, described above (#2), and it does fix the error.

EDIT 2: After some digging, I think I have a better answer now. The short answer is that the meaning of -Wconversion in GCC has shifted somewhat.

From the GCC 4.2.4 manual:

-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.

You have a prototype. That expression would be different in the absence of a prototype (e.g. pre-ANSI/ISO C, AKA Traditional C) which would convert it to int.

From the GCC 4.6.4 manual:

-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.

Note the silence on prototypes, since it's been standard C practice for a very long time now to have them. Apparently GCC had a few hangers on who still had to build Traditional C, so for those folks -Wtraditional-conversion produces these warnings since GCC 4.3.

So it's not spurious. It's apparently not a bug. It's just not what people expect these days.

Community
  • 1
  • 1
ldav1s
  • 15,885
  • 2
  • 53
  • 56
  • If I cast it, as such: `os(rx,ita,(short int)charge)` I still get the warning/error. However, if I create a new variable, as such, it compiles without the warning/error: `double ost(double rx,double ita,short int charge,int PQN,int AQN) { short int Z=charge; return os(rx,ita,Z)*exp(0.5*(log(2.0*AQN+1.0)-lgamma((double)PQN-AQN)-lgamma((double)(PQN+AQN+1)))+lgamma((double)PQN)); }` – Mannix Apr 29 '14 at 17:31
  • Is there a way to prevent it from being converted to an `int`? Also, on a different platform (Linux), it does not give this warning. Here's the compiler version on the Linux platform: gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 – Mannix Apr 29 '14 at 17:50
  • 2
    Honestly, this just sounds like a spurious warning from that particular compiler version. There's nothing wrong with your C. If you accept a `short int` into a function and immediately pass it to another function that also accepts a `short int` there shouldn't be any implicit conversion going on because the types at both sides of the equation are the same. – Crowman Apr 29 '14 at 18:29
  • It sure looks like a bug to me. As you quote, the old `-Wconversion` will "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" (emphasis mine). If you pass a `short int` to a function with a correct prototype and that function accepts a `short int`, then you shouldn't get a type conversion at all. Therefore, the prototype doesn't cause a type conversion and even the old `-Wconversion`, as it is documented, should not warn about it. – Crowman Apr 29 '14 at 22:10
  • That being said, if it is a bug, it's possibly not fixed, since `-Wtraditional-conversion` on gcc version 4.7.2 does still give the same warning. – Crowman Apr 29 '14 at 22:25
  • "If you pass a short int to a function with a correct prototype and that function accepts a short int, then you shouldn't get a type conversion at all." OK. That treats part one of the test of of the parameter into `os` -- it's a `short int`. Now part two of the test treats `os` as if it did _not_ have a prototype. In this case the parameter is treated as an `int`. Since an `int` and a `short int` have different widths, it prints an error. It appears that `-Wtraditional-conversion` is the flag to perform this test in current versions of GCC. – ldav1s Apr 29 '14 at 22:37
  • But the prototype still does not cause a type conversion. Say if the prototype declared `long`, instead of `short int`. *Then* the prototype would cause a type conversion (i.e. `short` to `long`), and it would be different from the one that would occur without the prototype (i.e. `short` to `int`). Who knows what the original intent was, but this is what the documentation appears to be talking about. – Crowman Apr 29 '14 at 22:41
  • OK, so `charge` is a `long` now. With the prototype, the parameter is a `long`. Without the prototype, the parameter is transformed by the usual unary conversions (for Traditional C) to a `long`. `long` = `long` -- so no error. Which is what I observe with gcc 3.4.6. – ldav1s Apr 29 '14 at 23:01
  • That's not what I see. I get the same error when you change it to a `long`, as you'd expect. The error goes away when you change it to an `int`, since then it matches the default promotion. – Crowman Apr 29 '14 at 23:20
  • @Paul, without the prototype, `charge` is promoted to `int`; but with a prototype, it is not promoted. So the prototype suppresses a type conversion, we could say that this is sloppy language in the GCC documentation and they intended "if the type conversion (if any) differs from what it would have been if the function were not prototyped". If that's what they meant, then this code matches that; however that doesn't detract from the fact that the code is 100% correct . – M.M Apr 30 '14 at 03:11
  • It seems to me that this warning is in the same category as "comparing signed and unsigned" - it can be generated by correct code as well as by incorrect code, but the compiler is unable or unwilling to perform code analysis to see if the actual code could have unexpected behaviour (in OP's case, there cannot be a problem). – M.M Apr 30 '14 at 03:12
  • @Matt: I suspect you're right. The man page says "The absence of these prototypes when compiling with traditional C would cause serious problems." As far as I can tell, this warning comes on when you have prototypes which do cause type conversions, to warn you against taking them out if you're going to compile in traditional C. Which, since you have them in there to begin with, seems like a bit of a head-scratcher. – Crowman Apr 30 '14 at 03:24
  • @Paul, I think the code is correct even in a non-prototyped functions. There would only be an error if a prototype is visible to the calling code but not visible to the function implementation, or vice versa. – M.M Apr 30 '14 at 03:25
  • @MattMcNabb: I think you were right originally, if you defined a function taking a `short` and failed to prototype it, you'd actually end up passing it an `int`, which would give you problems, I think. – Crowman Apr 30 '14 at 03:28
  • I think I was wrong originally, I re-read the standard to check, and if the function is non-prototyped then the function implementation reads an `int` from the arguments and converts it back to `short int` , for a `short int` parameter. If this were not the case then pre-ANSI code would never have been able to pass a `char` to a function, for example – M.M Apr 30 '14 at 03:30
  • @MattMcNabb: Do you have a reference for that handy? I get the point about `char` arguments in pre-ANSI, but how is the function implementation going to know whether the function was prototyped in the caller's source file? – Crowman Apr 30 '14 at 03:39
  • @Paul, it can't know, that's the point. It's up to you to make sure (typically by having the implementation and the caller both include the same header with the function declaration, be it prototyped or not). Re. standard text, http://stackoverflow.com/questions/1255775/default-argument-promotions-in-c-function-calls – M.M Apr 30 '14 at 03:41
  • @MattMcNabb: OK, I think we have been talking at cross purposes, here. If by "prototype" you're including the kind that appears *in the function definition* (and the wording in the standard does), then it makes a lot more sense. When I said "if you defined a function taking a `short` and failed to prototype it", I meant define the function as `int myfunc(short a) { ... }` (i.e. giving it a prototype) and not have a prototype in the caller, which I think is correct, just inaccurately worded. You're right. – Crowman Apr 30 '14 at 03:49
  • To clarify, `int myfunc(short a) {...}` is a prototyped function definition, a non-prototyped one would be `int myfunc(a) short a; { ... }` – M.M Apr 30 '14 at 03:50
  • @MattMcNabb: Yes, agreed. The warning makes more sense, now, since the implementation file could generate it all by itself, to remind to you include a prototype in the caller. Nevertheless, there is no type conversion going on in this code, implicit or otherwise, as you say. – Crowman Apr 30 '14 at 03:51
  • @PaulGriffiths I see where I went wrong now. The conversion of the `short int` should have been to `int`. However on the 32-bit image I'm running gcc 3.4.6 on `long` = `int` apparently. On a 64-bit CentOS 5, `long` != `int`, and I get errors for both `short` -> `short` and `short` -> `long` when compiling for 64-bit. When compiling for 32-bit I get a `short` -> `short` error but no `short` -> `long` error which is consistent at least. I still see no bug here. – ldav1s Apr 30 '14 at 05:50
  • @ldav1s: Yeah, it does seem to be careful about saying conversions that are specifically "changing the width", which could vary. I still think it's at best very sloppily worded documentation, and even allowing that it only seems to warn about about something that it already knows you already remembered to do. I suppose you could always get the prototype wrong (or include one when you shouldn't), but if you did, you'd have problems even if there wasn't any type conversion. Whether or not it's a bug I'll leave to the gcc team. – Crowman Apr 30 '14 at 07:27
4

Ubuntu 12.04

Putting the code shown in the question into file ost.c, adding #include <math.h>, and compiling on an Ubuntu 12.04 derivative with GCC 4.9.0 and the options:

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Wold-style-declaration -Werror -Wconversion -c ost.c
$

I get no warnings at all. I get the same result with GCC 4.6.3 and 4.8.2 (also on the same machine).

With your options, I get one problem: lgamma() isn't declared. Change -ansi to -std=c99 and that problem goes away (lgamma() is standard in C99, but not C89 which -ansi requests).

Mac OS X 10.9.2 Mavericks

Testing on Mac OS X 10.9.2 Mavericks with various compilers (newer versions than the one you're using) similarly shows no problem. I tested:

$ gcc --version
gcc (GCC) 4.9.0
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ /usr/bin/gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix
$ clang --version
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix
$

Basically, what you're running into is a bug in the compiler that has been fixed in newer versions of the compiler.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Why is it not helpful to know that the problem is fixed in current versions of the compilers on both Linux and Mac? Or what else about this answer is unhelpful? – Jonathan Leffler Apr 29 '14 at 21:53
  • 1
    The behavior seen by the OP is _not_ a bug. You should be able to reproduce it using `-Wtraditional-conversion` instead of `-Wconversion` as described in the [GCC 4.3 changes](http://www.gnu.org/software/gcc/gcc-4.3/changes.html) document. – ldav1s Apr 30 '14 at 15:33
  • @ldav1s You might want to refer to [this](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35214) and [this](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9072). – devnull May 01 '14 at 03:53
  • @devnull, I don't disagree that their original implementation is clunky, but even there [GCC bug 9072](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9072) (the patch that creates how `-Wconversion` is currently) is marked as an enhancement, not a bug. – ldav1s May 01 '14 at 04:44