Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 16 Feb 2014 15:09:51 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        David Chisnall <theraven@FreeBSD.org>
Cc:        svn-src-head@FreeBSD.org, svn-src-all@FreeBSD.org, src-committers@FreeBSD.org, Dimitry Andric <dim@FreeBSD.org>, Bruce Evans <brde@optusnet.com.au>
Subject:   Re: svn commit: r261916 - head/sys/dev/xen/console
Message-ID:  <20140216134006.R788@besplex.bde.org>
In-Reply-To: <8C22DE47-60F2-47C0-938D-590324818872@FreeBSD.org>
References:  <201402151237.s1FCbRnh000507@svn.freebsd.org> <20140216035823.I2978@besplex.bde.org> <8C22DE47-60F2-47C0-938D-590324818872@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sat, 15 Feb 2014, David Chisnall wrote:

> On 15 Feb 2014, at 17:02, Bruce Evans <brde@optusnet.com.au> wrote:
>
>> Why?  There are hundreds if not thousands of static inline functions in
>> headers, and most of these functions are not always used, so there would
>> be [hundreds if not thousands] * [number of #includes] compiler warnings
>> if compilers warned about things like this.  They could handle include
>> files specially, but shouldn't.
>
> They do, and absolutely should, handle include files separately.  If you have a static inline function in a header that is not used in a specific compilation unit, then that is a little bit of extra work for the compiler as it has to parse it without it being used, but it is not a problem.  It is a safe assumption that it is used by at least one compilation unit and so is not dead code (and even if it isn't yet, it is part of an API, and so removing it would be an error).

No, there is no difference between an include file and a main source file,
except for STDC headers in hosted implementations.  Main source files may
by included.  Compilers shouldn't police the dubious style of this.

> In contrast, a static inline function in the main source file for a compilation unit is definitely a bug.  It is obviously dead code.  It is likely that it either should have been removed when all callers were deleted, or should not have been static but accidentally was.

It is _not_ obviously a bug.  The main source file may have been carefully
designed to be #included, with only the necessary #ifdef uglyness for this.
Something like:

main.c:
/*
  * Broken compilers might barf if this is file used in the usual way by
  * including it, but I refuse to work around the brokenness by ifdefing
  * standards-conforming code.
  */
static inline int
foo(int _x)
{
 	...
}

/* Squillions more non-ifdefed static inline functions. */

/* Public functions need ifdefs of course. */
#ifdef WANT_MAIN
int main(void)
{
 	...
}
#endif

/*
  * In C99, 'inline' is only a hint, so non-online functions strictly don't
  * need ifdefs any more that inline ones.  However, some compilers will
  * keep plain static functions, and we need this feature.  Ifdefing these
  * functions is not as essential as for public functions, but we do it to
  * avoid duplication of the functions.
  *
  * It is unclear how to write the ifdefs for this.  The above main() is
  * only used in a special testing context, so we can use the condition
  * used to configure this context (the tests of it probably started in
  * a makefile).  Whether this file is used being used as an include file
  * is probably determined by the same configuration info, but we don't
  * want to depend on that, and we want to show the full awfulness of the
  * potential ifdef ugliness, so we use a separate macro for each function.
  * The number of functions that need this should of course be minimized.
  *
  * XXX need more magic to keep static functions in a portable way as
  * renamed public functions.
  */
#ifdef WANT_BAR
static int
bar(int x)
/* The ifdef messes allow naming x without an underscore. */
{
 	...
}
#endif

For extra complications, use even worse style where the main source
file is never used directly, but is included deeply nested and turns
back into the main source file (with public functions) in 1 compilation
unit.  The ifdefs in the above example already support this, since they
don't have any magic depending on the nesting level.

For more practical examples of the brokenness, consider the effect of
preprocessing.  I often use cpp -P and compile the result.  I use -P
here to mainly to remove unwanted #line statements.  Preprocessing
must keep static inline functions, and -P removes any hints about
whether they are in include files.  Of course, using preprocessed
output is outside of the C standard, but it is useful.  The compiler
might have further bugs involving special cases for preprocessed output
in files name *.i.  But the preprocessed output may be placed in files
named *.c.  That and removing #line statements and unportable directives
with hints about include files if the preprocessed output is the final
version of the file and/or is compiled by a compiler that doesn't support
*.i.

Similar considerations apply to the -W[no-]system-headers magic.  Errors
in system headers (other than ones related to the preprocessing pass)
become just as fatal as anywhere else after cpp -P.

Similar considerations apply to machine-generated main source files
from other sources.  yacc and lex are well known for generating
unreachable break statements that lint doesn't like, except lint ignores
these by default and has a flag -b to report them.  You don't want to
either complicate the generating program or have special compiler flags
and complications in makefiles to set the flags to do the same thing
for compilers.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140216134006.R788>