Compilation with warning

Go To Last Post
28 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's an odd one. I'm trying to decide whether this is a bug or not, before I take it to the IDE/compiler maker, so no names, no pack drill.

 

This bloody thing took me all afternoon playing with a machine code I really don't grok to find out... what was happening was a routine was returning a 64-bit result; that result was being passed to another routine - it expecting a 64 bit result, but it was receiving only 32 bits of it; the top half was a sign extended bottom half. Which caused me furiously to think... this was on a 32 bit processor.

 

In a minimum demonstration, you need two two .c files and two .h files.

 

// main.c

#include <stdio.h>
#include <stdint.h>
#include "main.h"
#include "bug.h"

void main (void)
{
	static uint64_t	res;
	uint64_t		big = 0x1234567890abcdef;
	
	res = show_bug (big);
	res = test (res);
	return;
}

uint64_t show_bug (uint64_t in)
{
	return in+1;
}
// main.h 

#ifndef _MAIN_H_
#define _MAIN_H_

uint64_t show_bug (uint64_t in);

#endif
// bug.c

#include "bug.h"

uint64_t test (uint64_t in)
{
	uint64_t res;
	
	res = show_bug (in);
	return res;
}
// bug.h

#ifndef _BUG_H_
#define _BUG_H_

#include <stdint.h>

uint64_t test (uint64_t in);

#endif

The significant thing here is that bug.c does not include a prototype for show_bug(). That throws a warning (easily lost in the clutter of the hundred-odd files in the project I was compiling), and not repeated in further compilations which don't touch the guilty file. But it builds and links...

 

When you come to run it, what happens is that at the end of main, res = 0xffffffff89abcdf1.

 

What has happened is that the compiler, not finding a prototype for show_bug() in bug.c, assumes that it's a default routine with integer in and out. As a result, it does a sign extend on the return value bottom 32 bits. The linker seems happy to find the routine as defined in main.c and raises no objection.

 

So as far as I can see, things are happening according to the C spec - but should the linker throw an error in this circumstance? Is this a bug?

 

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:
the compiler, not finding a prototype for show_bug() in bug.c, assumes that it's a default routine with integer in and out

Yes, it would.

 

barnacle wrote:
things are happening according to the C spec

Indeed

 

barnacle wrote:
should the linker throw an error in this circumstance?

No.

 

The linker just cares about matching up names to addresses - it neither knows nor cares anything about function return or parameter types.

 

C++ would be different - as parameter types, etc, get "mangled" into the name...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:

The significant thing here is that bug.c does not include a prototype for show_bug(). That throws a warning (easily lost in the clutter of the hundred-odd files in the project I was compiling), and not repeated in further compilations which don't touch the guilty file. But it builds and links...

 

What has happened is that the compiler, not finding a prototype for show_bug() in bug.c, assumes that it's a default routine with integer in and out. As a result, it does a sign extend on the return value bottom 32 bits. The linker seems happy to find the routine as defined in main.c and raises no objection.

 

That is how all C compiler/linkers have worked since I started using C back in the 70's, you need to seek out warnings from your compiles!

I always do a complete rebuild of a project and search the build log for any warnings if not highlighted by the compiler! 

 

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

If you want something you've never had...

...you must be willing to do something you've never done!

Lets go Brandon!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ki0bk wrote:

 

I always do a complete rebuild of a project and search the build log for any warnings if not highlighted by the compiler! 

 

 

Or use a compiler flag to have warnings be treated as errors (-Werror for gcc), and crank the warning level up as high as you can tolerate (I always use -Wall, -Wextra, and a smattering of others).

build-avr-gcc: avr-gcc build script

toolchain-avr-gcc: CMake toolchain for cross compiling for the AVR family of microcontrollers

avr-libcpp: C++ standard library partial implementation (C++17 only) for use with avr-gcc/avr-libc

picolibrary: C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

picolibrary-microchip-megaavr: picolibrary HIL for megaAVR microcontrollers

picolibrary-microchip-megaavr0: picolibrary HIL for megaAVR 0-series microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:
So as far as I can see, things are happening according to the C spec

Depends on which version.

C89/90 allowed implicit function declaration.

C99 doesn't allow it (I can't find where within the all the legalese it makes this clear, but it's in there somewhere) and so has to issue a 'diagnostic'.

But diagnostic can be a warning or an error or whatever, so compiler can give a warning and still carry on and produce a compiled output if it wants.

With gcc whether you get a warning (and it carries on) or an error (and it doesn't) will depend on what combination of options you use.

 

For example, I find this gives error

gcc -Wall -Wextra -O2 -std=c99 -pedantic-errors -o main  main.c bug.c

 

main.c:287:6: error: return type of ‘main’ is not ‘int’ [-Wmain]
 void main (void)
      ^
bug.c: In function ‘test’:
bug.c:7:8: error: implicit declaration of function ‘show_bug’ [-Wimplicit-function-declaration]
  res = show_bug (in);

 

whereas this gives warning

 

gcc -Wall -Wextra -O2 -o main  main.c bug.c 

 

main.c:287:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
 void main (void)
      ^
bug.c: In function ‘test’:
bug.c:7:8: warning: implicit declaration of function ‘show_bug’ [-Wimplicit-function-declaration]
  res = show_bug (in);

 

If you use -Werror then as stated in above post any warning will become error.

Bottom line is, don't ignore warnings.

This is easier if you require that all code must be warning free, then you can easily spot when you get a new one.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you twiddled the compiler to turn all warnings into errors?  That would have halted your build.

 

If this were GCC, I might suggest:

-pedantic-errors

    Give an error whenever the base standard (see -Wpedantic) requires a diagnostic, in some cases where there is undefined behavior at compile-time and in some other cases that do not prevent compilation of programs that are valid according to the standard. This is not equivalent to -Werror=pedantic, since there are errors enabled by this option and not enabled by the latter and vice versa.

This would also make GCC complain about your main() of type void, but that can be avoided with -Wno-main:

-Wmain

    Warn if the type of main is suspicious. main should be a function with external linkage, returning int, taking either zero arguments, two, or three arguments of appropriate types. This warning is enabled by default in C++ and is enabled by either -Wall or -Wpedantic.

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That throws a warning (easily lost in the clutter of the hundred-odd files in the project I was compiling)

 You got the warning; your fault for not noticing it.  Maybe use "-Werror" ?

bug.c:9:8: warning: implicit declaration of function 'show_bug' [-Wimplicit-function-declaration]

presumably it's "only" a warning because of the vast quantity of pre-prototyped C code.

(There is also -Werror=implicit-function-declaration if you have too many other warnings in your code base.  Although they're probably all worth looking into.)

 

Last Edited: Tue. Apr 7, 2020 - 11:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:

What has happened is that the compiler, not finding a prototype for show_bug() in bug.c, assumes that it's a default routine with integer in and out. As a result, it does a sign extend on the return value bottom 32 bits. The linker seems happy to find the routine as defined in main.c and raises no objection. So as far as I can see, things are happening according to the C spec - but should the linker throw an error in this circumstance? Is this a bug?

 

No. 

 

Not according to the modern C spec, which has been in effect since 1999. You are not allowed to call undeclared functions in C. Full prototypes are still not required, but at least a non-prototype declaration must be present.

 

The compiler gave you a "warning" message... But C language does recognize the concept of a "warning". In C language there are only diagnostic messages and nothing else. What you got in this case is standard-mandated diagnostic message, which indicates that your code contains a constraint violation, i.e. your code is invalid. Constraint violation is what we call an "error" in everyday language. The fact that the compiler gave you a diagnostic message (any diagnostic message) effectively frees it from any responsibility for your code. Your program is not a valid C program. C language does not recognize it as a C program. Whatever executable code your compiler generates after issuing a standard-mandated diagnostic message is entirely a personal fantasy of your specific compiler. As far as C language is concerned, the behavior is undefined.

 

This undefined behavior is what you observed in your experiment.

 

The fact the your compiler decided to report it as a "warning" is nothing more than a quirk of your specific C compiler (or just a consequence of a "loose" compiler setup). You have to run your compiler in `-pedantic-errors` mode (using GCC nomenclature) to force it to report "errors" as "errors". In default mode it reports many "errors" as "warnings".

Dessine-moi un mouton

Last Edited: Wed. Apr 8, 2020 - 12:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
You got the warning; your fault for not noticing it.  Maybe use "-Werror" ?

 

`-Werror` will turn all "warning" messages into compilation-stopping events, i.e. "errors", including those that are "just warnings". This is an unjustified overkill.

 

Compilers in GCC family have a dedicated switch `-pedantic-errors`, whose purpose is to turn into "errors" only those "warnings" that are actually "errors" from language spec point of view. Meanwhile, innocent "just warnings" will remain "warnings". This is a much more sensible idea.

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought the various "-pedantic" switches would also complain about various common and safe C constructs that were not strictly allowed by the language?

Which warnings that show up without -Wall do you find deserve ignoring?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:

I thought the various "-pedantic" switches would also complain about various common and safe C constructs that were not strictly allowed by the language?

 

If a construct is not allowed by the language, then pedantically speaking it is not a "C construct". The switch is named rather aptly...

 

Also, I'm almost sure that virtually any of the aforementioned "common and safe constructs that are not strictly allowed by the language", can be easily modified to fit perfectly well into the boundaries of what's allowed. So, `-pedantic` doesn't really target those "common and safe constructs". It targets programmer's laziness.

 

westfw wrote:
Which warnings that show up without -Wall do you find deserve ignoring?

 

When it comes to "just warnings", behavior of GCC in its default configuration has always been rather dynamic: some warnings constantly appeared, others disappeared. I'd say that the current state of affairs looks pretty good to me.

 

One thing came to mind though

 

extern int a = 15;
warning: 'a' initialized and declared 'extern'

This is uncalled for. The compiler assumes that I don't know what I'm doing.

 

Anyway, I don't advocate ignoring warnings. I'd prefer if the compiler reported errors (i.e. syntax violations and constraint violations) as "errors", at least in its default configuration.

Dessine-moi un mouton

Last Edited: Wed. Apr 8, 2020 - 03:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks folks.

 

To clarify; this is not GCC. Further, I appreciate that the warning should have been caught by me; my normal practice is to remove all warnings, but in this case I had a large number of files being compiled, and as this file wasn't touched after the first compilation run it didn't subsequently show. A make clean showed it, as expected.

 

The general opinion seems to be that this is not an error. I can live with that. Indeed, the only reason it was an issue is that I was using parameters larger than the default int size; had I been passing 32 bit values this would never have shown up.

 

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:
as this file wasn't touched after the first compilation run it didn't subsequently show. A make clean showed it, as expected.

That catches me out surprisingly often:

 

Work through list of warnings ...

Press F7

========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========

Hurrah - No warnings.

"Build - Rebuild Solution"

Oh for  f*&k's sake!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:
To clarify; this is not GCC.

 

Quite a few different C compilers have the same kind of "loose" approach to error checking (in default mode). The main reason for this is large volume of legacy code written in the era of dinosaurs, when code like this was indeed legal.

 

barnacle wrote:
The general opinion seems to be that this is not an error.

 

That "general opinion" is incorrect. C language specification is quite unambiguous about this: this is a constraint violation, i.e. an error.

 

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

barnacle wrote:

The general opinion seems to be that this is not an error.

 

 

That "general opinion" is incorrect. C language specification is quite unambiguous about this: this is a constraint violation, i.e. an error.

I think the 'error' being referred to here is whether there is a bug or ot

barnacle wrote:

but should the linker throw an error in this circumstance? Is this a bug?

which is as expected, it's not a bug.

And it's also not an bug that the compiler issues a warning and carries on regardless.

I think it would only be a bug if the compiler didn't emit anything at all (assuming C99 or later mode)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:

I thought the various "-pedantic" switches would also complain about various common and safe C constructs that were not strictly allowed by the language?

Which warnings that show up without -Wall do you find deserve ignoring?

 

Here's one of my pet peeves about avr-g++:

empty.cpp:1:11: error: '::main' must return 'int'
 void main() {}

There's no parent process on an AVR, so main should have no return value.

However, compile the same program as C, and you get no errors.

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

apcountryman wrote:

ki0bk wrote:

 

I always do a complete rebuild of a project and search the build log for any warnings if not highlighted by the compiler! 

 

 

Or use a compiler flag to have warnings be treated as errors (-Werror for gcc), and crank the warning level up as high as you can tolerate (I always use -Wall, -Wextra, and a smattering of others).

 

-Wall is OK for newbies.  For experienced developers, it generates warnings for lots of perfectly valid code.  Here's an example:

gcc -Os -g  -Wall   print.c   -o print
print.c: In function 'print':
print.c:6:5: warning: suggest parentheses around assignment used as truth value
[-Wparentheses]
     while (c = *s++) putchar(c);
     ^~~~~

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:
-Wall is OK for newbies.  For experienced developers, it generates warnings for lots of perfectly valid code.  Here's an example:

gcc -Os -g  -Wall   print.c   -o print
print.c: In function 'print':
print.c:6:5: warning: suggest parentheses around assignment used as truth value
[-Wparentheses]
     while (c = *s++) putchar(c);
     ^~~~~

Why not just do what it suggests?

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:

-Wall is OK for newbies.  For experienced developers, it generates warnings for lots of perfectly valid code.  Here's an example:

gcc -Os -g  -Wall   print.c   -o print
print.c: In function 'print':
print.c:6:5: warning: suggest parentheses around assignment used as truth value
[-Wparentheses]
     while (c = *s++) putchar(c);
     ^~~~~

You're entitled to your opinion of course, but I thnk that's nonsense.

All the experienced developers I know would use -Wall (typically plus -Wextra plus a bunch of others, if needs be disable a specific warning here or there on an individual basis) and either do what the warning suggests ie.

while ((c = *s++))

or

while ((c = *s++) != '\0')

or not even bother with c

while (*s != '\0')

{

    putchar(*s++);

}

 

Thw whole point of -Wall is that it enables 'all reasonably non controversial' warnings so doesn't generally generate lots of annoying and pointless false positives, although this depends of course on what you think of as annoying and pointless :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:

There's no parent process on an AVR, so main should have no return value.

However, compile the same program as C, and you get no errors.

Yeah but it's adhering to the rules of the C standard. I forget the exact details but there is a variant of C that does not require a return from main() but equally it does not have access to standard libc functionality like printf() et al either.

 

EDIT: quick memory jog... so it seems:  http://www.iso-9899.info/n1570.html#4.p6  that the two variants are "hosted" and "fresstanding". What's more for the latter:  http://www.iso-9899.info/n1570.html#5.1.2.1  it seems the entry function doesn't even have to be called "main()"

Last Edited: Wed. Apr 8, 2020 - 02:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

-pedantic is for testing whether a program complies with a C standard.

-pedantic-errors is for pacifying compiler-testers that do not regard warnings as diagnostics.

 

A compiler is not required to stop on every error.

Admittedly most do and I am not aware of any recent exceptions.

From a language standpoint, all diagnostics are equivalent.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Yeah but it's adhering to the rules of the C standard. I forget the exact details but there is a variant of C that does not require a return from main() but equally it does not have access to standard libc functionality like printf() et al either.

 

 

EDIT: quick memory jog... so it seems:  http://www.iso-9899.info/n1570.html#4.p6  that the two variants are "hosted" and "fresstanding". What's more for the latter:  http://www.iso-9899.info/n1570.html#5.1.2.1  it seems the entry function doesn't even have to be called "main()"

GCC/G++ has -ffreestanding, but as you say it omits more than just the int main() requirement.

 

GCC has -Wno-main to suppress the warning about main's return type, but that has no effect with G++.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

ralphd wrote:
-Wall is OK for newbies.  For experienced developers, it generates warnings for lots of perfectly valid code.  Here's an example:

gcc -Os -g  -Wall   print.c   -o print
print.c: In function 'print':
print.c:6:5: warning: suggest parentheses around assignment used as truth value
[-Wparentheses]
     while (c = *s++) putchar(c);
     ^~~~~

Why not just do what it suggests?

 

Adding the superfluous parentheses reduces readability.  I was even confused at first why it was a warning.  Then I remembered decades ago that some students learning C would mix up == with =.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GCC (AVR) does have those OS_main and noreturn attributes. I've often wondered if they could relax the main return warning when those were employed. Esp. "__attribute__((noreturn)) int main() {}" which otherwise reads like an oxymoron !

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
the two variants are "hosted" and "fresstanding".

 

Microcontrollers are prominent examples of freestanding environment, within which no requirements on the type of `main` apply. If an AVR-specific compiler complains about the return type of `main`, it most likely means that someone configured it improperly. Someone forgot to tell it that we are compiling for a freestanding environment.

 

(Although, there's a possibility this is a conscious decision, made because, say, switching to freestanding mode kills some other useful diagnostics.)

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assuming you have an endless while(1) loop in main, the compiler probably won't generate code that actually does a 'return 0', in which case 'int main(void) isn't costing much, I suppose it is costing you an unnecessary couple of bytes on the stack for a return address if the startup code does a 'call main'.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:
Adding the superfluous parentheses reduces readability.  I was even confused at first why it was a warning.  Then I remembered decades ago that some students learning C would mix up == with =.
One need not bee a student to be a bad typist.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
Microcontrollers are prominent examples of freestanding environment, within which no requirements on the type of `main` apply. If an AVR-specific compiler complains about the return type of `main`, it most likely means that someone configured it improperly.
The default for avr-gcc is hosted.

Freestanding does more than just relax requirements on main.

main is not even required.

Where the program starts is implementation- and environment-defined.

Also, some library functions are not required.

If encountered, the compiler will treat them as ordinary functions.

It will not make any optimizations based on its knowledge of said functions.

Moderation in all things. -- ancient proverb