Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

This statement may fall through - how?

360 views
Skip to first unread message

mathog

unread,
Apr 29, 2020, 8:18:57 PM4/29/20
to
The code snippet below my signature generates this message when compiled:

g++ -DHAVE_CONFIG_H -I. -I../..
-I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
-I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
-Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
-Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
.deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
fasta_formatter.cpp:105:9: error: this statement may fall through
[-Werror=implicit-fallthrough=]
usage();
~~~~~^~
fasta_formatter.cpp:107:3: note: here
case 'i':
^~~~

This is with gcc 8.3.1. Is there really some way that usage() can avoid
hitting exit() or is the compiler just emitting a general warning
without actually looking at usage()? I can see where this warning would
apply if usage() was in some other compilation unit, then the compiler
could not reasonably figure it out. However here it is literally the
function before the problem function! If I were to add a "break;"
after the usage() call a smarter compiler would most likely complain
about an unreachable statement. (In this case adding the "break;" does
eliminate the current warning without triggering the other.)

Thanks,

David Mathog


void usage()
{
printf("%s",usage_string);
exit(0);
}

void parse_command_line(int argc, char* argv[])
{
int opt;

while ( (opt = getopt(argc, argv, "i:o:hw:te") ) != -1 ) {

//Parse the default options
switch(opt) {
case 'h':
usage();

case 'i':
input_filename = optarg;
break;

case 'o':
output_filename = optarg;
break;

case 'w':
flag_requested_output_width = atoi(optarg);
if ( flag_requested_output_width < 0 )
errx(1,"Invalid value (%s) for requested width [-w]", optarg);
break ;

case 't':
flag_output_tabular = true ;
break;

case 'e':
flag_output_empty_sequences = true;
break;

default:
exit(1);
}
}
}

Ian Collins

unread,
Apr 29, 2020, 8:32:36 PM4/29/20
to
On 30/04/2020 12:18, mathog wrote:
> The code snippet below my signature generates this message when compiled:
>
> g++ -DHAVE_CONFIG_H -I. -I../..
> -I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
> -I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
> -Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
> -Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
> .deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
> fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
> fasta_formatter.cpp:105:9: error: this statement may fall through
> [-Werror=implicit-fallthrough=]
> usage();
> ~~~~~^~
> fasta_formatter.cpp:107:3: note: here
> case 'i':
> ^~~~
>
> This is with gcc 8.3.1. Is there really some way that usage() can avoid
> hitting exit() or is the compiler just emitting a general warning
> without actually looking at usage()?

If you are using gcc; __attribute__ ((noreturn))?

--
Ian

Tim Rentsch

unread,
Apr 29, 2020, 8:58:37 PM4/29/20
to
mathog <mat...@caltech.edu> writes:

> [..warning 'statement may fall through'..]
>
> [...] Is there really some way that usage() can
> avoid hitting exit() or is the compiler just emitting a general
> warning without actually looking at usage()?
>
> void usage()
> {
> printf("%s",usage_string);
> exit(0);
> }
>
> [call to 'usage()' just before a 'case' label]

_Noreturn void usage(){ ... }

gcc-8 -std=c11 -pedantic-errors -Wall -Wextra ...


Keith Thompson

unread,
Apr 29, 2020, 9:03:27 PM4/29/20
to
And/or if you're using C++11 or later: _Noreturn?

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

Scott Lurndal

unread,
Apr 29, 2020, 9:25:15 PM4/29/20
to
mathog <mat...@caltech.edu> writes:
>The code snippet below my signature generates this message when compiled:
>
>g++ -DHAVE_CONFIG_H -I. -I../..
>-I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
> -I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
>-Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
>-Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
>.deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
>fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
>fasta_formatter.cpp:105:9: error: this statement may fall through
>[-Werror=implicit-fallthrough=]
> usage();
> ~~~~~^~
>fasta_formatter.cpp:107:3: note: here
> case 'i':
> ^~~~
>
>This is with gcc 8.3.1. Is there really some way that usage() can avoid
>hitting exit() or is the compiler just emitting a general warning
>without actually looking at usage()?

Even if it did, it's possible for the programmer to
provide his own 'exit()' function in a different compilation
unit. The compiler can't assume that just because usage()
called 'exit', it actually terminates the process.

Keith Thompson

unread,
Apr 29, 2020, 9:37:15 PM4/29/20
to
sc...@slp53.sl.home (Scott Lurndal) writes:
> mathog <mat...@caltech.edu> writes:
>>The code snippet below my signature generates this message when compiled:
>>
>>g++ -DHAVE_CONFIG_H -I. -I../..
>>-I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
>> -I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
>>-Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
>>-Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
>>.deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
>>fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
>>fasta_formatter.cpp:105:9: error: this statement may fall through
>>[-Werror=implicit-fallthrough=]
>> usage();
>> ~~~~~^~
>>fasta_formatter.cpp:107:3: note: here
>> case 'i':
>> ^~~~
>>
>>This is with gcc 8.3.1. Is there really some way that usage() can avoid
>>hitting exit() or is the compiler just emitting a general warning
>>without actually looking at usage()?
>
> Even if it did, it's possible for the programmer to
> provide his own 'exit()' function in a different compilation
> unit. The compiler can't assume that just because usage()
> called 'exit', it actually terminates the process.

Yes, the compiler can assume that.

N1570 7.1.3p1:

All identifiers with external linkage in any of the following
subclauses (including the future library directions) and errno are
always reserved for use as identifiers with external linkage.

Defining your own function named "exit" with external linkage has
undefined behavior.

Of course a compiler isn't *required* to assume that (the language
doesn't require it to dig into the implementation of the usage()
function to determine that it can never return).

Ian Collins

unread,
Apr 29, 2020, 10:01:23 PM4/29/20
to
On 30/04/2020 13:03, Keith Thompson wrote:
> Ian Collins <ian-...@hotmail.com> writes:
>> On 30/04/2020 12:18, mathog wrote:
>>> The code snippet below my signature generates this message when compiled:
>>>
>>> g++ -DHAVE_CONFIG_H -I. -I../..
>>> -I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
>>> -I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
>>> -Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
>>> -Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
>>> .deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
>>> fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
>>> fasta_formatter.cpp:105:9: error: this statement may fall through
>>> [-Werror=implicit-fallthrough=]
>>> usage();
>>> ~~~~~^~
>>> fasta_formatter.cpp:107:3: note: here
>>> case 'i':
>>> ^~~~
>>>
>>> This is with gcc 8.3.1. Is there really some way that usage() can avoid
>>> hitting exit() or is the compiler just emitting a general warning
>>> without actually looking at usage()?
>>
>> If you are using gcc; __attribute__ ((noreturn))?
>
> And/or if you're using C++11 or later: _Noreturn?

Ah yes, better still I'd forgotten that one!

--
Ian

Bonita Montero

unread,
Apr 30, 2020, 4:33:50 AM4/30/20
to
A break wouldn't hurt.

Kenny McCormack

unread,
Apr 30, 2020, 4:49:02 AM4/30/20
to
In article <r8e2h3$999$1...@news.albasani.net>,
Bonita Montero <Bonita....@gmail.com> wrote:
>A break wouldn't hurt.

Isn't there also something you can do with comments? Something like
putting: /* Fall Through */
in there will inhibit the warning?

Anyway, I suppose the best idea is to use the attribute thing, if it is
available.

--
I'm building a wall.

Bonita Montero

unread,
Apr 30, 2020, 4:54:21 AM4/30/20
to
> Anyway, I suppose the best idea is to use the attribute thing, if it is
> available.

The best thing is to insert a "break" because it is not confusing.

James Harris

unread,
Apr 30, 2020, 5:03:57 AM4/30/20
to
If the goal is "best" then not embedding an exit call at all may be even
better. It's often better structured to unwind back to main and return
from there.

If printing a usage message on request a return 0 could be correct. But
if the usage message is printed due to parameter errors then one could
return 1 (or perhaps some other nonzero) ... from main.


--
James Harris

Bonita Montero

unread,
Apr 30, 2020, 5:08:11 AM4/30/20
to
> If the goal is "best" then not embedding an exit call at all may be even
> better. It's often better structured to unwind back to main and return
> from there.

C programming is usually dirty, why not dirty like this ? ;-)

Kenny McCormack

unread,
Apr 30, 2020, 5:10:42 AM4/30/20
to
In article <r8e3nj$6q3$1...@news.albasani.net>,
You know what they say about opinions...

--
The only thing Trump's made great again is Saturday Night Live.

Keith Thompson

unread,
Apr 30, 2020, 5:15:49 AM4/30/20
to
Or EXIT_FAILURE.

Bonita Montero

unread,
Apr 30, 2020, 5:18:11 AM4/30/20
to
>> The best thing is to insert a "break" because it is not confusing.

> You know what they say about opinions...

That's not an opinion but a fact. You have to know what usage()
does to know that it doesn't need a break. But you don't know
it at the first place when reading the code.

James Harris

unread,
Apr 30, 2020, 7:27:46 AM4/30/20
to
On 30/04/2020 10:15, Keith Thompson wrote:
> James Harris <james.h...@gmail.com> writes:
>> On 30/04/2020 09:54, Bonita Montero wrote:
>>>> Anyway, I suppose the best idea is to use the attribute thing, if it is
>>>> available.
>>>
>>> The best thing is to insert a "break" because it is not confusing.
>>
>> If the goal is "best" then not embedding an exit call at all may be
>> even better. It's often better structured to unwind back to main and
>> return from there.
>>
>> If printing a usage message on request a return 0 could be
>> correct. But if the usage message is printed due to parameter errors
>> then one could return 1 (or perhaps some other nonzero) ... from main.
>
> Or EXIT_FAILURE.

I didn't know that existed but it and EXIT_SUCCESS are curious
definitions because while a good use of header files is to ensure that
constants agree between C modules what main returns to may not be C.

If whatever called main was not written in C it may not have been able
to parse stdlib.h or even be built against the same versions of such
constants. Therefore, unless there is a way for the caller to find out
what EXIT_FAILURE and EXIT_SUCCESS the main function was built with
there would strictly speaking be no way for the caller to test for exit
success or failure!

IOW, it may be best to return 0 or 1.


--
James Harris

Ben Bacarisse

unread,
Apr 30, 2020, 8:42:20 AM4/30/20
to
James Harris <james.h...@gmail.com> writes:

> On 30/04/2020 10:15, Keith Thompson wrote:
>> James Harris <james.h...@gmail.com> writes:
<cut>
>>> If printing a usage message on request a return 0 could be
>>> correct. But if the usage message is printed due to parameter errors
>>> then one could return 1 (or perhaps some other nonzero) ... from main.
>>
>> Or EXIT_FAILURE.
>
> I didn't know that existed but it and EXIT_SUCCESS are curious
> definitions because while a good use of header files is to ensure that
> constants agree between C modules what main returns to may not be C.
>
> If whatever called main was not written in C it may not have been able
> to parse stdlib.h or even be built against the same versions of such
> constants. Therefore, unless there is a way for the caller to find out
> what EXIT_FAILURE and EXIT_SUCCESS the main function was built with
> there would strictly speaking be no way for the caller to test for
> exit success or failure!

They are designed so that the C programmer does not need to know how the
hosting system interprets the value returned by main. If that
(basically the "OS") is what you mean by "whatever called main" then
they do the job perfectly adequately. But if you are talking about some
other way in which main is called, I don't see how anything in the C
standard could possible help.

> IOW, it may be best to return 0 or 1.

If the OS interprets all return values less that 256 as "success" then
returning 1 does not signal a failure.

--
Ben.

James Kuyper

unread,
Apr 30, 2020, 10:26:29 AM4/30/20
to
A value returned from main() is handled as if it were passed to exit().
When exit(status) is called, "If the value of status is zero or
EXIT_SUCCESS, an implementation-defined form of the status successful
termination is returned. If the value of status is EXIT_FAILURE, an
implementation-defined form of the status unsuccessful termination is
returned. Otherwise the status returned is
implementation-defined." (7.22.4.4p5)

Note in particular that EXIT_SUCCESS is neither required to expand to an
expression with a value of 0, nor is it prohibited from doing so. The
same is true of EXIT_FAILURE and 1. EXIT_FAILURE does have to expand
into an expression with a value different from both 0 the value of the
expansion of EXIT_SUCCESS, otherwise it couldn't produce a different
termination status. What happens if you return 1 is covered only by the
last sentence of that paragraph.

You can reasonably expect an implementation to choose the form for each
of those termination statuses in a way that is appropriate for the
target platform (and you should complain if it isn't). It's neither
necessary nor sufficient for the program that checks the termination
status of a C program to know anything about C. It's both necessary and
sufficient that it know how to use the language-specific interfaces to
the platform-specific ways of checking the termination status.

For instance, on unix-like systems, you can use wait(), waitpid(), or
waitid() to wait for the status of a process to change, and to check
what the new status is. The termination status is reported in an int,
but that int does NOT necessarly have the same value that the C program
passed to the exit() routine. If WIFEXITED(termination_status) is true,
then WEXITSTATUS(termination_status) gives you the 8 low-order bits of
the value that was passed to exit(). The rest of that value is
unavailable. BSD has defined a standard meaning for exit statuses of 0
and any number from 64 to EX__MAX, which is currently 78 on my machine -
see sysexits.h for details. Other values can have meanings defined by
the user. If your code takes advantage of that fact, any code that
checks the termination status will need to know what meaning your code
attaches to each of those values.

On other platforms, with little or nothing in the way of an operating
system or a display, a successful exit might be indicated by causing a
light to turn green, while unsuccessful status may be indicated by
causing a light to turn red.

Richard Damon

unread,
Apr 30, 2020, 10:30:19 AM4/30/20
to
On 4/30/20 7:27 AM, James Harris wrote:
By the standard, return 0 needs to always be considered a success, and
if the OS doesn't treat a 0 as success, the runtime between you and the
OS will need to map the code 0 to something the OS will consider a success.

return EXIT_SUCCESS will also return to the OS something that it has
defined to be a success, and that is really the preferred way.

return EXIT_FAILURE will return to the OS something that is has defined
to be a failure.

If you need to return more specific success and failure codes, then you
need to rely on the documentation of the OS AND the implementation (as
it may be doing some remapping of the return value to met the
requirements of the standard, particularly if the return value of 0 is
not defined by the OS as success).

saying return 1 is by definition dependent on the OS and the
implementations documentation.

If something called the full program (including the implementation
provided wrapper for main, if it is needed), then that caller needs to
abide by the convention of the OS and expect things like the OS would
expect, or use knowledge of the implementation.

If something just calls main, and bypassed the provided wrapper, than
that caller needs to do any needed initialization that the wrapper
provided, and thus have knowledge of the implementation. In many cases
this may include clearing out blocks of memory that need to be zeroed,
but to save space in the load file, and speed in loading, are not zeroed
just by loading the file.

James Harris

unread,
Apr 30, 2020, 12:44:04 PM4/30/20
to
On 30/04/2020 15:26, James Kuyper wrote:
> On 4/30/20 7:27 AM, James Harris wrote:

...


> The termination status is reported in an int,
> but that int does NOT necessarly have the same value that the C program
> passed to the exit() routine.

OK. I understood that the value returned from main would be reported as
the termination status for small values (for varying definitions of
small) but if I am reading a certain draft standard correctly (n1256) I
gather that even that's not guaranteed.


> If WIFEXITED(termination_status) is true,
> then WEXITSTATUS(termination_status) gives you the 8 low-order bits of
> the value that was passed to exit(). The rest of that value is
> unavailable. BSD has defined a standard meaning for exit statuses of 0
> and any number from 64 to EX__MAX, which is currently 78 on my machine -
> see sysexits.h for details.

Interestingly, on my current system sysexits.h includes a definition for
a usage error:

#define EX_USAGE 64 /* command line usage error */

Trying to get my head round this, does that mean that such a value could
be - or even should be - returned from main (by return or exit) if a
problem is found with the usage:

* on this particular system?
* on any BSD later than a certain date?
* on non-BSD systems?

Notably, sysexits.h at the top says

* This include file attempts to categorize possible error
* exit statuses for system programs, notably delivermail
* and the Berkeley network.

It's notable that it says "/attempts/ to categorize" and its
applicability to "system programs" so I wonder if these codes are
relevant for user-written software or even if they are used by all
system programs.

More to the point, these appear to be OS statuses rather than C ones. If
a C program is intended to run under various OSes then it sounds as
though the only portable exit statuses are 0 and the EXIT_ definitions
in stdlib.h.


--
James Harris

James Kuyper

unread,
Apr 30, 2020, 1:02:10 PM4/30/20
to
On 4/30/20 12:43 PM, James Harris wrote:
> On 30/04/2020 15:26, James Kuyper wrote:
>> On 4/30/20 7:27 AM, James Harris wrote:
>
> ...
>
>
>> The termination status is reported in an int,
>> but that int does NOT necessarly have the same value that the C program
>> passed to the exit() routine.
>
> OK. I understood that the value returned from main would be reported as
> the termination status for small values (for varying definitions of
> small) but if I am reading a certain draft standard correctly (n1256) I
> gather that even that's not guaranteed.

Correct. Only "successful" and "unsuccessful" termination status need be
distinguished, and the method used to distiguish them is unspecified.

>> If WIFEXITED(termination_status) is true,
>> then WEXITSTATUS(termination_status) gives you the 8 low-order bits of
>> the value that was passed to exit(). The rest of that value is
>> unavailable. BSD has defined a standard meaning for exit statuses of 0
>> and any number from 64 to EX__MAX, which is currently 78 on my machine -
>> see sysexits.h for details.
>
> Interestingly, on my current system sysexits.h includes a definition for
> a usage error:
>
> #define EX_USAGE 64 /* command line usage error */
>
> Trying to get my head round this, does that mean that such a value could
> be - or even should be - returned from main (by return or exit) if a
> problem is found with the usage:
>
> * on this particular system?
> * on any BSD later than a certain date?
> * on non-BSD systems?

BSD systems are both required to provide sysexits.h containing such
macros, and to have some system utilities that recognize those codes and
deal with them appropriately. Neither of those things is guaranteed for
non-BSD systems. I would use them unconditionally only in code that was
meant to used only on BSD systems. I would make their use conditional on
BSD support, only if it using them provided a sufficiently valuable
benefit, and that benefit was something I'd be willing to forego in
order to allow the code to be ported to non-BSD systems. That would be
an unusual situation, but not an impossible one.

> Notably, sysexits.h at the top says
>
> * This include file attempts to categorize possible error
> * exit statuses for system programs, notably delivermail
> * and the Berkeley network.
>
> It's notable that it says "/attempts/ to categorize" and its
> applicability to "system programs" so I wonder if these codes are
> relevant for user-written software or even if they are used by all
> system programs.

User-written software is free to use them, if you wish.

> More to the point, these appear to be OS statuses rather than C ones. If

Correct - the exit status that is passed to exit() by your code is a C
construct. The termination status that exit() returns to the OS is
OS-specific. exit() provides the interface for connecting those two
status codes.

> a C program is intended to run under various OSes then it sounds as
> though the only portable exit statuses are 0 and the EXIT_ definitions
> in stdlib.h.

Correct.

Spiros Bousbouras

unread,
Apr 30, 2020, 1:34:43 PM4/30/20
to
I remember a time this was discussed and someone mentioned an older
operating system where the various exit values had very different meanings
from what one is used to in a UNIX system. Anyone knows or remembers
details ?

Lew Pitcher

unread,
Apr 30, 2020, 1:54:10 PM4/30/20
to
On April 30, 2020 13:34, Spiros Bousbouras wrote:

> On Thu, 30 Apr 2020 13:01:56 -0400
> James Kuyper <james...@alumni.caltech.edu> wrote:
>> On 4/30/20 12:43 PM, James Harris wrote:
>
>> Correct - the exit status that is passed to exit() by your code is a C
>> construct. The termination status that exit() returns to the OS is
>> OS-specific. exit() provides the interface for connecting those two
>> status codes.
>
> I remember a time this was discussed and someone mentioned an older
> operating system where the various exit values had very different meanings
> from what one is used to in a UNIX system. Anyone knows or remembers
> details ?

IIRC, OpenVMS was one of those operating systems; Wikipedia mentions that
"In OpenVMS, success is indicated by odd values and failure by even values.
The value is a 32 bit integer with sub-fields: control bits, facility
number, message number and severity. Severity values are divided between
success (Success, Informational) and failure (Warning, Error, Fatal)."


>> > a C program is intended to run under various OSes then it sounds as
>> > though the only portable exit statuses are 0 and the EXIT_ definitions
>> > in stdlib.h.
>>
>> Correct.

--
Lew Pitcher
"In Skills, We Trust"

Scott Lurndal

unread,
Apr 30, 2020, 1:54:48 PM4/30/20
to
Spiros Bousbouras <spi...@gmail.com> writes:
>On Thu, 30 Apr 2020 13:01:56 -0400
>James Kuyper <james...@alumni.caltech.edu> wrote:
>> On 4/30/20 12:43 PM, James Harris wrote:
>
>> Correct - the exit status that is passed to exit() by your code is a C
>> construct. The termination status that exit() returns to the OS is
>> OS-specific. exit() provides the interface for connecting those two
>> status codes.
>
>I remember a time this was discussed and someone mentioned an older
>operating system where the various exit values had very different meanings
>from what one is used to in a UNIX system. Anyone knows or remembers
>details ?

VMS, perhaps? Many exit codes, both at the command interpreter
level and the system interface level (e.g. SS$_NORMAL).

chad

unread,
Apr 30, 2020, 1:55:38 PM4/30/20
to
Why not just break statement after usage?

mathog

unread,
Apr 30, 2020, 4:13:54 PM4/30/20
to
You do in this case because the usage() function is literally right
before the parse_command_line() function which has this switch statement.

Keith Thompson

unread,
Apr 30, 2020, 4:21:28 PM4/30/20
to
Lew Pitcher <lew.p...@digitalfreehold.ca> writes:
> On April 30, 2020 13:34, Spiros Bousbouras wrote:
>> On Thu, 30 Apr 2020 13:01:56 -0400
>> James Kuyper <james...@alumni.caltech.edu> wrote:
>>> On 4/30/20 12:43 PM, James Harris wrote:
>>
>>> Correct - the exit status that is passed to exit() by your code is a C
>>> construct. The termination status that exit() returns to the OS is
>>> OS-specific. exit() provides the interface for connecting those two
>>> status codes.
>>
>> I remember a time this was discussed and someone mentioned an older
>> operating system where the various exit values had very different meanings
>> from what one is used to in a UNIX system. Anyone knows or remembers
>> details ?
>
> IIRC, OpenVMS was one of those operating systems; Wikipedia mentions that
> "In OpenVMS, success is indicated by odd values and failure by even values.
> The value is a 32 bit integer with sub-fields: control bits, facility
> number, message number and severity. Severity values are divided between
> success (Success, Informational) and failure (Warning, Error, Fatal)."

Note that a status of 0 on OpenVMS would denote failure, but exit(0)
still denotes success. The C runtime implementation treats 0 as a
special case, translating it to return 1 to the environment.

exit(1) (common for UNIX-based programs) would still return a status
indicating success. EXIT_FAILURE is defined as some even value.
EXIT_SUCCESS is *probably* 0, but I'm not sure of that.

mathog

unread,
Apr 30, 2020, 4:29:54 PM4/30/20
to
On 4/29/20 6:03 PM, Keith Thompson wrote:
> Ian Collins <ian-...@hotmail.com> writes:
>> On 30/04/2020 12:18, mathog wrote:
>>> The code snippet below my signature generates this message when compiled:
>>>
>>> g++ -DHAVE_CONFIG_H -I. -I../..
>>> -I/usr/common/modules/el8/x86_64/software/libgtextutils/0.7-CentOS-vanilla/include/gtextutils
>>> -I../../src/libfastx -g -O2 -Wall -Wextra -Wformat-nonliteral
>>> -Wformat-security -Wswitch-default -Wswitch-enum -Wunused-parameter
>>> -Wfloat-equal -Werror -DDEBUG -g -O1 -MT fasta_formatter.o -MD -MP -MF
>>> .deps/fasta_formatter.Tpo -c -o fasta_formatter.o fasta_formatter.cpp
>>> fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
>>> fasta_formatter.cpp:105:9: error: this statement may fall through
>>> [-Werror=implicit-fallthrough=]
>>> usage();
>>> ~~~~~^~
>>> fasta_formatter.cpp:107:3: note: here
>>> case 'i':
>>> ^~~~
>>>
>>> This is with gcc 8.3.1. Is there really some way that usage() can avoid
>>> hitting exit() or is the compiler just emitting a general warning
>>> without actually looking at usage()?
>>
>> If you are using gcc; __attribute__ ((noreturn))?
>
> And/or if you're using C++11 or later: _Noreturn?
>

The software package was released Jan 2014 so I'm going to guess that
the default standard for the compiler was not at that point C++11.
So this is likely yet another case of newer compiler defaults to newer
language standard, uncovers language standard change as warning/error at
compilation. In that era the g++ default was likely

gnu++98
gnu++03
GNU dialect of -std=c++98. This is the default for C++ code.


That said while exit() may be Noreturn in certain language versions

https://en.cppreference.com/w/cpp/utility/program/exit

how would shutting down the program result in a fall through to the next
switch statement, which was the warning observed?

Thanks

Spiros Bousbouras

unread,
Apr 30, 2020, 5:10:01 PM4/30/20
to
I assumed that the "C++11" Keith wrote was a typo and he meant "C11". If this
really is about C++ then why are you asking here and not on comp.lang.c++ ?

> That said while exit() may be Noreturn in certain language versions
>
> https://en.cppreference.com/w/cpp/utility/program/exit
>
> how would shutting down the program result in a fall through to the next
> switch statement, which was the warning observed?

As has been said , GCC does not know that the exit() in your code is the
standard exit() , it could be some other exit() which doesn't shut the
programme down. Perhaps GCC could conclude that , taking also into account
the command line options including those which get passed to the linker , but
it wouldn't be worth the trouble since you can simply put _Noreturn (I'm
guessing GCC supported something like that before it became standard).

Keith Thompson

unread,
Apr 30, 2020, 6:19:28 PM4/30/20
to
Spiros Bousbouras <spi...@gmail.com> writes:
> On Thu, 30 Apr 2020 13:29:41 -0700
> mathog <mat...@caltech.edu> wrote:
[...]
> I assumed that the "C++11" Keith wrote was a typo and he meant "C11". If this
> really is about C++ then why are you asking here and not on comp.lang.c++ ?

Yes, it was a typo (more precisely a thinko). I meant C11. (C++ has
had the [[noreturn]] attribute since C++11, but I wasn't referring
to that.)

I'll note, however, that the article that started this thread used a
command like:

g++ [lots of options] fasta_formatter.cpp

It's likely that the same issues apply to C, but the OP probably should
have either posted to comp.lang.c++ or used a C example (which might be
as simple as changing the command and file name). The rules for C and
C++ *might* be different, and the behavior of gcc and g++ could very
well be different.

>> That said while exit() may be Noreturn in certain language versions
>>
>> https://en.cppreference.com/w/cpp/utility/program/exit
>>
>> how would shutting down the program result in a fall through to the next
>> switch statement, which was the warning observed?

It wouldn't. The question is whether the compiler (a) knows that and
(b) is required to know that.

The OP reported this error message:

fasta_formatter.cpp: In function ‘void parse_command_line(int, char**)’:
fasta_formatter.cpp:105:9: error: this statement may fall through
[-Werror=implicit-fallthrough=]
usage();
~~~~~^~
fasta_formatter.cpp:107:3: note: here
case 'i':
^~~~

Fallthough is not a constraint violation in C (nor does it violate
any rule in C++), so gcc or g++ would not normally treat it as a
fatal error. It can do so with certain command-line options (such
as -Werror), but I didn't see any such options in the original post.
I can't easily reproduce the behavior, partly because the original
post did not provide a complete program (a number of #includes and
declarations were missing).

> As has been said , GCC does not know that the exit() in your code is the
> standard exit() , it could be some other exit() which doesn't shut the
> programme down. Perhaps GCC could conclude that , taking also into account
> the command line options including those which get passed to the linker , but
> it wouldn't be worth the trouble since you can simply put _Noreturn (I'm
> guessing GCC supported something like that before it became standard).

As has also been said, the compiler *can* assume that the exit
function being called is the standard exit function declared in
<stdlib.h>. See C11/N1570 7.1.3.

mathog

unread,
Apr 30, 2020, 7:22:02 PM4/30/20
to
On 4/30/20 2:09 PM, Spiros Bousbouras wrote:
> I assumed that the "C++11" Keith wrote was a typo and he meant "C11". If this
> really is about C++ then why are you asking here and not on comp.lang.c++ ?

The package is mostly C, I just didn't notice that this file happened to
be C++.

I have reduced it to a test case in C which does the same thing:


cat >test.c <<'EOD'
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void usage(void);
void parse_command_line(int argc, char* argv[]);


int main(int argc, char* argv[]){
parse_command_line(argc,argv);
printf("done\n");
exit(EXIT_SUCCESS);
}

void usage()
{
printf("usage string\n");
exit(0);
}

void parse_command_line(int argc, char* argv[])
{
int opt;

while ( (opt = getopt(argc, argv, "i:o:hw:te") ) != -1 ) {
switch(opt) {
case 'h':
usage();

default:
printf("anything else\n");
break;
}
}
}
EOD
gcc -Wall -Wextra -o test test.c
test.c: In function ‘parse_command_line’:
test.c:28:9: warning: this statement may fall through
[-Wimplicit-fallthrough=]
usage();
^~~~~~~
test.c:30:7: note: here
default:
^~~~~~~

It only does it with -Wextra. These do the same thing:

gcc -Wall -Wextra -std=c99 -D_XOPEN_SOURCE -o test test.c
gcc -Wall -Wextra -std=c11 -D_XOPEN_SOURCE -o test test.c
gcc -Wall -Wextra -std=c17 -D_XOPEN_SOURCE -o test test.c

This is with gcc 8.3.1.

Regards,

David Mathog



Keith Thompson

unread,
Apr 30, 2020, 8:28:09 PM4/30/20
to
Now it's a non-fatal warning. Since it's not a required diagnostic,
this is not a conformance issue. Since you specifically requested
non-required diagnostics by compiling with "-Wextra", one might
argue that you're just getting what you asked for.

Apparently gcc does not analyze the implementation of the usage()
function to determine that it cannot return. It's not required to
do so, and I'm not surprised that it doesn't. I certainly wouldn't
expect it to do that analysis at optimization level 0, but even
with "-O3", gcc either doesn't do that analysis, or doesn't use
the results to decide not to issue the warning.

There are several ways you could inform the compiler that usage()
doesn't return.

If I add "_Noreturn" to the declaration of usage, the warning goes
away. If you don't want to assume C11, there are gcc-specific ways
to do the same thing (and you could use #ifdef's to choose how to
do this).

Ian Collins

unread,
Apr 30, 2020, 8:44:19 PM4/30/20
to
clang suggests some of the fixes already proposed here:

test.c:30:8: warning: unannotated fall-through between switch labels
[-Wimplicit-fallthrough]
default:
^
test.c:30:8: note: insert '__attribute__((fallthrough));' to silence
this warning
default:
^
__attribute__((fallthrough));
test.c:30:8: note: insert 'break;' to avoid fall-through
default:
^
break;
--
Ian.

mathog

unread,
May 1, 2020, 1:28:19 PM5/1/20
to
On 4/30/20 5:44 PM, Ian Collins wrote:
>        __attribute__((fallthrough));
> test.c:30:8: note: insert 'break;' to avoid fall-through
>        default:
>        ^
>        break;

If some future version of the compiler decides to analyze usage() and
sees that it cannot return, will that generate the "unreachable
statement" warning? Hmm, I'm guessing not, because modifying usage() to:

void usage()
{
printf("usage string\n");
exit(0);
printf("unreachable\n");
}

(and leaving the rest of test.c alone) did not generate an unreachable
code warning when compiled like so:

gcc -Wall -Wextra -std=c99 -D_XOPEN_SOURCE -o test test.c
gcc -Wall -Wextra -std=c11 -D_XOPEN_SOURCE -o test test.c


there was only the preexisting fall through warning. If exit()
is already marked Noreturn in c11 why didn't it warn about the
unreachable printf?

A warning that statements follow an exit() is something that would
actually be useful, since that would only happen by mistake.


chad

unread,
May 1, 2020, 3:43:20 PM5/1/20
to
Have you considered the fact that comp?uter programming might not be for you
0 new messages