svn commit: r286281 - head/sys/kern

Bruce Evans brde at optusnet.com.au
Tue Aug 4 11:03:53 UTC 2015


On Tue, 4 Aug 2015, Ed Schouten wrote:

> 2015-08-04 10:51 GMT+02:00 Edward Tomasz Napierala <trasz at freebsd.org>:
>>   Mark vgonel() as static.  It was already declared static earlier;
>>   no idea why compilers don't warn about this.
>
> That's because according to the standard, those keywords are meant to pile up:
>
> static void foo(void);
> _Noreturn void foo(void);
> void foo(void) {
>  ...
> }
>
> is the same as:
>
> static _Noreturn void foo(void) {
>  ...
> }

This is a bug in the standard.  You can build up enormously complicated
declarations, with the full declaration not visible in any one of the
parts.  E.g.:

int foo();
int foo(int (*foo1()));
int foo(int (*foo1(int (*foo2()))));
int foo(int (*foo1(int (*foo2(int (*foo3)())))));
/* ... */

Every declaration in this sequence completes the type a little more,
but the type is never complete.

To obfuscate this further, remove the function parameter names, or
use different ones in each prototype.

To complicate this further, there are many possibilities.  E.g., make
foo() have several function args and (partially) complete each of these
independently.  Or use attributes to give even more independence in
the steps.

I don't know of any useful use for this except to complete function
definitions in K&R or bad code.  The code might have "int foo();" in
it and you want to add a complete prototype without changing the
original declaration.  The complete prototypes should be declared in
one step to avoid further obfuscations.

Compilers have very bad support for warning about this.  -Wredundant-decls
is broken in gcc from when it was born up to at least gcc-4.2.1.  The
above declarations are non-redundant non-redeclarations, but gcc warns
that they are redundant redaclaration.  -Wredundant-decls is even more
broken in clang.  It doesn't warn even if all of the above declarations
are repeated so that every declaration in the second copy is redundant.

"Building up" doesn't work for all parts of declarations.  In particular,
the rules for linkage and storage class are confusing without building
up, and building up doesn't work for them.  Consider:

     static int foo;
     int foo;

vs

     int foo;
     static int foo;

According to TenDRA C90, the former is valid, but according to gcc and
clang it is invalid.  The latter is invalid according to all 3 compilers,
so building up by adding the static is always invalid.  I only trust
TenDRA here.  The former is a valid obfuscation (the opposite of building
up) for all 3 compilers if foo is replaced by foo().

Now change 'int foo;' to 'extern int foo;' in the former.  Then all 3
compilers accept it.

extern is generally more confusing than static, especially in
combination with inline.  For static inline, the most common obfuscation
is like this one for vgonel.  You declare the function as inline in its
prototype but not in its function body.  This makes it inline if it is
static in both.  With static in neither and extern in either, both or
neither, the behaviour is even more confusing.

Bruce


More information about the svn-src-head mailing list