Author Topic: Weird compiler/preprocessor issue  (Read 1764 times)

0 Members and 1 Guest are viewing this topic.

Offline crmTopic starter

  • Contributor
  • Posts: 11
  • Country: us
Weird compiler/preprocessor issue
« on: October 20, 2025, 01:53:30 pm »
I use macros quite a bit, but never noticed anything like that. The behavior is different for an explicit chunk of code vs an identical macro expansion. Older versions seem to generate two errors (which is what I expect). But newer ones only one.

The file:
Code: [Select]
#include <stdint.h>
volatile struct { uint16_t v; } s;
int main(void) {
#define M(x) if (x < -50) x++
  if (s.v < -50) s.v++;
  M(s.v);
}

An old compiler:
Code: [Select]
AtmelArmV4.8.1429\arm-gnu-toolchain\bin\arm-none-eabi-gcc.exe -c -Wextra file.c
file.c: In function 'main':
file.c:5:3: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   if (s.v < -50) s.v++;
   ^
file.c:6:3: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   M(s.v);
   ^

A more recent one:
Code: [Select]
arm-gnu-toolchain-14.2\bin\arm-none-eabi-gcc.exe -c -Wextra file.c -fdiagnostics-plain-output
file.c: In function 'main':
file.c:5:11: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   if (s.v < -50) s.v++;
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 638
  • Country: be
Re: Weird compiler/preprocessor issue
« Reply #1 on: October 20, 2025, 02:05:59 pm »
Show the generated assembly code.
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 638
  • Country: be
Re: Weird compiler/preprocessor issue
« Reply #2 on: October 20, 2025, 02:25:59 pm »
Looks like something got broken since gcc version 6. Preprocessing and compiling shows two warnings:

Code: [Select]
arm-none-eabi-gcc -E limited-range.c | arm-none-eabi-gcc -xc -Wextra -c -
limited-range.c: In function 'main':
limited-range.c:5:13: warning: comparison is always false due to limited range of data type [-Wtype-limits]
    5 |     if (s.v < -50) s.v++;
      |             ^
limited-range.c:6:13: warning: comparison is always false due to limited range of data type [-Wtype-limits]
    6 |     M(s.v);
      |             ^

clang shows both.
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Weird compiler/preprocessor issue
« Reply #3 on: October 20, 2025, 04:43:33 pm »
I don't know, but I wouldn't be surprised if this is intentional, or a side effect of an intentional change.  I can certainly imaging there are situations where certain warnings from macros expansions are common enough that you want to suppress them.  For instance an admittedly dumb example:

Code: [Select]
#define FITS_IN_INT16_FIELD(x) ((x) >= 32768LL && (x) < 32768ULL)

This is intended to be used in routines for packing data for serialization and checks whether any integral value x can be cast to an int16 without overflow.  If type type of 'x' is 16 bit or smaller, or unsigned, one or both of these checks are expected to compile to nothing, but for larger types it should turn into an actual test.  But if I write this code explicitly, then I definitely want the warning because the type of x is then fixed. 

I'm not saying it's a good idea or that anyone should use my above example.  But I can see why warnings might be suppressed in a situation like this.[/code]
« Last Edit: October 20, 2025, 07:45:54 pm by ejeffrey »
 

Offline crmTopic starter

  • Contributor
  • Posts: 11
  • Country: us
Re: Weird compiler/preprocessor issue
« Reply #4 on: October 20, 2025, 07:39:53 pm »
It crossed my mind that perhaps this was intentional. If it is, I don't think I like that. I would rather deal with inconveniences than see explicit code deviate from otherwise identical code that is a result of a macro expansion.

 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Weird compiler/preprocessor issue
« Reply #5 on: October 20, 2025, 08:54:37 pm »
Quote
I would rather deal with inconveniences than see explicit code deviate from otherwise identical code that is a result of a macro expansion.

I don't have a strong opinion on this warning either way, but I think you are approaching this incorrectly.  We aren't talking about the semantics of the generated code (which are quite clear in both cases), but of the warnings.  The purpose of a warning is to tell a human that the code they wrote may have a different meaning than what they expected.   So it is impossible to talk about warnings without inferring human intent. The original source code is the correct source to make inferences about the humans intent, not the output of the preprocessor.  So I think in general it is 100% OK to use the full context of the original source (pre-expansion) to decide what warnings are appropriate.  It goes both ways.  There are definitely cases where you would want to emit a warning for code from a macro expansion but not from literal code.  The most obvious ones are double side effects and cases where operator precedence breaks up a macro argument.

I think to actually make a determination about this specific case you would need to collect examples of cases where the warning was a false positive vs. genuine positive (i.e., intent matching behavior vs not).  You also have to consider how easy it is to rewrite the offending code to avoid the warning, and if so do you risk hiding other behavior that maybe should get a warning.

The ease of avoiding the warning is a big issue.  With explicit code it's essentially always trivial to just remove the always-true comparison.  With a macro expansion it is not.  And generally warnings are only useful if they are fixable, otherwise people end up just turning the warning off entirely.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17819
  • Country: fr
Re: Weird compiler/preprocessor issue
« Reply #6 on: October 20, 2025, 10:43:04 pm »
I checked and can confirm. That's "interesting". I don't really understand how that's even possible, unless GCC internally tags macro expansions, as it would otherwise occur to us that the compiler would compile preprocessed code and that the two steps are separated. It's possible that for compiling efficiency reasons, they have optimized the two passes and somehow that's leading to warnings not being handled for macro expansions, but that's a bit odd. I see no possible rationale if that was "intentional", I kinda doubt it is.

To see if that was maybe a general problem with warnings and macro expansions (which I doubted as I never personally ran into this oddity), I just tried the same code but with the following macro instead:
Code: [Select]
#define M(x) x /= 0and this properly emits the following warning:
Code: [Select]
<source>:6:16: warning: division by zero [-Wdiv-by-zero]
    6 | #define M(x) x /= 0
      |                ^~
<source>:10:3: note: in expansion of macro 'M'
   10 |   M(s.v);
      |   ^

So I'm afraid this may be an odd "edge" case with this particular "limited range"  warning. This would IMO warrant a bug report in GCC bugzilla. Since the piece of code required to show the issue is extremely short and self-contained, it will have no problem getting accepted as a bug report.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Weird compiler/preprocessor issue
« Reply #7 on: October 20, 2025, 10:52:24 pm »
Starting with GCC 12, the -Wtype-limits warning is suppressed for constant expressions and macro expansions in certain cases, as part of refined warning heuristics.

There is no way I know of to override this, not even explicitly enabling -Wtype-limits or -Wall.

You can however get the warning if you use an inline function instead of a macro

https://godbolt.org/z/h6jKTse3e
 
The following users thanked this post: 5U4GB

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17819
  • Country: fr
Re: Weird compiler/preprocessor issue
« Reply #8 on: October 20, 2025, 11:00:53 pm »
Starting with GCC 12, the -Wtype-limits warning is suppressed for constant expressions and macro expansions in certain cases, as part of refined warning heuristics.

There is no way I know of to override this, not even explicitly enabling -Wtype-limits or -Wall.

You can however get the warning if you use an inline function instead of a macro

https://godbolt.org/z/h6jKTse3e

Ah, thanks for the info, although I have tested it down to GCC 8 and it still suppressed the OP's warning in the macro expansion, but not that other warning I tested.
So, at least for this specific warning, it goes back to at least GCC 8. You can see for yourself with goldbolt.

I don't think this kind of ad-hoc heuristics are a good idea at all. All the more that -Wtype-limits is only enabled with -Wextra, but not with -Wall alone, at least again since GCC 8.2, which is the oldest supported by godbolt (at least that I saw).

Possibly comes from maintainers of large code bases (like Linux?) absolutely insisting on compiling with -Wextra and finding out that they had a ton of such warning coming from macros. But again, I don't like ad-hoc "features" like this at all.
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Weird compiler/preprocessor issue
« Reply #9 on: October 21, 2025, 06:39:06 am »
I checked and can confirm. That's "interesting". I don't really understand how that's even possible, unless GCC internally tags macro expansions, as it would otherwise occur to us that the compiler would compile preprocessed code and that the two steps are separated.

It has to track macro expansions to give sane warnings no matter what.  You see that in your division by zero example where not only does it show the original code rather than the preprocessor output, but the message says "note: in expansion of macro 'M'".

Quote
I don't think this kind of ad-hoc heuristics are a good idea at all

At some level, all warnings are ad hoc heuristics. 

Quote
Possibly comes from maintainers of large code bases (like Linux?) absolutely insisting on compiling with -Wextra and finding out that they had a ton of such warning coming from macros. But again, I don't like ad-hoc "features" like this at all.

That would be a totally fine justification in my book.  If in real code bases that heavily use macros this shows up frequently as a false positive, that's a great reason to exclude it. 

Quote from: brucehoult
You can however get the warning if you use an inline function instead of a macro

Which illustrates the difference.  For the inline function, the parameter x is a fixed type, uint16_t..  For the macro, the type is not fixed within the definition of the macro.

You get the warning from the macro if the type is explicit within the definition:

Code: [Select]
#include <stdint.h>
volatile struct { uint16_t v; } s;
#define M(x) if (x < -50) x++
#define N(x) do {uint16_t y = x; if (y < -50) x++; } while(0)

int main(void) {
  if (s.v < -50) s.v++;
  M(s.v);
  N(s.v);
}

gives warnings on lines 7 and 9 but not 8.
 

Offline hans

  • Super Contributor
  • ***
  • Posts: 1990
  • Country: 00
Re: Weird compiler/preprocessor issue
« Reply #10 on: October 21, 2025, 08:03:53 am »
Similar thing occurs in C++:

Code: [Select]
#include <stdint.h>
struct sx {
    volatile uint16_t v;
};
sx s;

template<typename T>
void cmp(T& v) {
    if (v < -50) v++;
}

int main(void) {
    cmp(s.v);
    return 0;
}

GCC 4.6.4:
Quote
<source>:9:5: warning: comparison is always false due to limited range of data type [-Wtype-limits]
GCC 5+ is silent. Just testing CLang trunk and CLang 3, both are silent, so I'm blindly presuming the versions in between are the same.

Meanwhile ICC 2021 gives the proper warning.

Also this gives no warning:
Code: [Select]
template<typename T>
void cmp(T& v) {
    do {
        T y = v;
        if (y < -50) v++;
     } while(0);
}
If variable y is explicit uint16_t, it does generate a warning. Using "auto" for y still makes it silent..

I compiled with -Wall and -Wextra.

Metaprogramming is a lot more common in C++, especially embedded where lots of helper functions get template-ized to help with runtime speed. You could do similar things with code macro expansions in C, but its a bit more tricky to maintain, so I typically opted for simple verbose code there.

I can't imagine compilers would track what is expanded code on purpose and then try to reduce the number of warnings presented to the user. People put on high amount of warnings on purpose to catch silly errors.
I surely hope this is not part of some kind of spec that compiler devs are simply adhering to. I would really like to see a pedantic kind of option for things like this ..
« Last Edit: October 21, 2025, 08:13:24 am by hans »
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Weird compiler/preprocessor issue
« Reply #11 on: October 22, 2025, 08:21:45 pm »
You can use -save-temps as an easy way to compile from an intermediate file, and the compiler no longer has any visibility to macros and therefore warns on all instances. If you want to see all the warnings anyway.

https://godbolt.org/z/PazdMq616


The longer term solution is to get away from macros.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17819
  • Country: fr
Re: Weird compiler/preprocessor issue
« Reply #12 on: October 22, 2025, 09:24:00 pm »
Some further testing shows that cppcheck oddly  has the same behavior as gcc with this particular warning. It emits a warning for the direct code, but not for the macro expansion. It doesn't do this for other types of warnings either such as divide by 0.

And yes, clang does emit the warning but it's a slightly different one:
Code: [Select]
warning: result of comparison of constant -50 with expression of type
      'volatile uint16_t' (aka 'volatile unsigned short') is always false [-Wtautological-constant-out-of-range-compare]

Note that the compiled code may lead to "incorrect" behavior (at least according to what's very likely intended by the programmer), so ignoring the warning or not getting any can be pretty bad, as it will follow arithmetic conversion as defined in the C standard.

In the OP's particular example, it should behave as expected (and thus be always false), as the rank of the unsigned integer uint16_t is less than that of the negative constant (which is of type int), so in this case, the uint16_t integer will get promoted to int (which can hold all values of uint16_t) and both will be compared and be always false as expected.

OTOH, if the unsigned integer had the same rank as int (so, unsigned int instead of uint16_t, for instance), then the constant -50 would get converted to unsigned int and thus would be converted into a large unsigned value, which would probably not do what you expect at all.

Cf. the "Usual arithmetic conversions" section in the C std.

And indeed... if you replace 'uint16_t v' with 'unsigned int v' in your struct definition, you get your warning back with the macro expansion. So they have likely considered that this latter case is much more severe than the former, as it can lead to incorrect behavior rather than just being always false (as expected), and so this warranted a warning more so than the former case, which in effect is just 'dead code'.
« Last Edit: October 22, 2025, 09:26:00 pm by SiliconWizard »
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Weird compiler/preprocessor issue
« Reply #13 on: October 22, 2025, 11:01:50 pm »
Quote
And indeed... if you replace 'uint16_t v' with 'unsigned int v' in your struct definition, you get your warning back with the macro expansion. So they have likely considered that this latter case is much more severe than the former, as it can lead to incorrect behavior rather than just being always false (as expected), and so this warranted a warning more so than the former case, which in effect is just 'dead code'.

And based on the examples I have seen, I think that's justified.  A warning about dead code should usually suggest that you remove it.  But when the dead code is in a macro it can't be removed without potentially affecting other uses of the macro.  All of the cases where the code is _always_ dead (such as when the type is fixed by the macro) seem to generate a warning.  A warning that can be easily foreseen to cause someone to introduce a bug is something that should be at least considered carefully.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->