Re: Mangled function prototypes (phantom arguments)

Alejandro Colomar <[email protected]> Thu, 28 May 2026 16:25:39 +0200
Newsgroups org.kernel.vger.linux-man
Message-ID <ahhJNnIgFN0YMANw@devuan>
Hi Michael,

On 2026-05-28T15:06:10+0200, Michael Kerrisk (man7.org) wrote:
> > > > > The problems have all resulted in prototypes adding a phantom first
> > > > > argument. See for example this prototype from read.2:
> > > > >
> > > > >        ssize_t read(size_t count;
> > > > >                     int fd, void buf[count], size_t count);
> > > >
> > > > This is valid C (except for the array of void, but that's a separate
> > > > topic).
> > >
> > > As we see, I'm not keeping up with my C :-).
> >
> > To be fair, this is a very little-known feature (even though it's a very
> > old one).  I (and a few others) are trying to make it more well known,
> > as it's quite interesting.
> 
> I don't think the Linux system call and C library manual pages are a
> good place to promote this obscure GNU feature. It is confusing
> people, including me. (I came to making this report because several
> people have reported this "bug" on various pages rendered at
> man7.org.)

I understand the initial confusion, but I believe once an initial period
is overcome, the extra information will be very helpful to the reader.

> Please consider reverting these changes.

I've been thinking about this topic for some time already, and I will
keep these changes.

> These markings use
> little-understood, nonportable syntax.

While they're little understood, and certainly not portable, they're
also easy to learn.

On the other hand, manual pages have --for a long time-- lacked a lot
of information about parameters that could be expressed in the synopses
easily.  This includes nullability of arguments, lenghts of arrays, and
overlapping of pointers.  Those are now documented with Clang's
_Nullable, GNU forward declarations of function parameters, and
'restrict'.  Of those, only 'restrict' is portable.

Having to document that information in the description would be (was)
counter-productive, as the information is difficult to find,
inconsistent across pages, and adds to the wall of text.  Having that
information in a consistent and concise way specified in the synopsis
allows reducing the wall of text, and is significantly less ambiguous.

This change has significantly improved my own reading of manual pages,
where I can now reasonably understand most APIs by just looking at the
synopsis.  I've also --like you-- received many reports of people
surprised by this syntax, but most of them seem to be okay with it once
I explain it to them and point them to GCC's documentation of the
feature.  And then there are several other users that have reported very
positive feedback regarding the array and nullability annotations in the
synopses, claiming that they're now much more informative, and thanking
me for the change.

I think it's overall a positive change.

> The manual page synopses should
> be in standard, portable C that is *easy* to understand.

This has never been fully true; we've always had exceptions to the rule.
We have counter-examples:

-  The old version of open(2) had two prototypes, which is not valid C
   (it's valid C++, though):

	int open(const char *pathname, int flags);
	int open(const char *pathname, int flags, mode_t mode);

   We agreed to change it to the real prototype some years ago:

	int open(const char *path, int flags, ...
	         /* mode_t mode */ );

   BTW, I'll soon have to start using this C++ notation for string
   functions, since ISO C23 has changed the API of things like
   strchr(3), to preserve the const qualifier in the return value.  This
   has been implemented in glibc 2.43.  Now, strchr(3) is specified (and
   implemented) as if:

	char *strchr(char *s, int c);
	const char *strchr(const char *s, int c);

   The actual ISO C specification uses 'QChar' to make it more compact:

	QChar *strchr(QChar *s, int c);

   However, 'QChar' is less known by the general public, so C++ syntax
   seems better for manual pages.  That is, we'll recover this
   exception that was once used in open(2).

   This is a breaking change that some users have noticed and reported.

-  We have pages where we mix constants with the prototype.  There are
   old examples of this, although the most common is the system calls
   that don't have a wrapper.

	int syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id);

-  The assert(3) macro was specified as taking a scalar (which is not
   a type):

	void assert(scalar expression);

   I've changed it to take a C99 'bool', which has the same effect.

	void assert(bool expression);


About being easy to understand, I believe you'll understand it forever
since now, since it's not inherently complex.  I hope the same is true
of other readers.


> 
> Thanks,
> 
> Michael

Cheers,
Alex

-- 
<https://www.alejandro-colomar.es>
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAABCgAdFiEES7Jt9u9GbmlWADAi64mZXMKQwqkFAmoYUF4ACgkQ64mZXMKQ
wqlulA//Uxg7/dW24ag3G+n3j5Zk/iDz4xL5oGO1B+B6PdSCZttDFCR5Ap/4u66t
JnHg8YyiywUmRXoypCEw2YCEHB1QF34Z+Y2/ISNaqnB+2zhzOugSyonlyXt1i0Bm
EK7aPT+0bUMUJdEA2yDSEsXru9PXSL4YW6syewViisON7jgFgv6i+N10G1qKWTBU
jgAn5WjyDXoYaSkwtAyfYEPdBkYEK83NrpbKU4TfA15g4OjJe8TjX+kjQXuJ1TlT
isXAkMtBEMWOUf0W3eust+daV69JsE4o65OSRLoOHGBKvl5JggmgrlgI+N1U9lVR
arhPylGGlIs2BtJirUEBPDx7OQt1Lie91r+gzIdmSU9zDqNGUkVHX8bRj7Ey3J9n
Po0gqT/OWwlX2kMHAfuS2hcHh+g8Q+LbJyzNEutfOB9aipnneKGaSrU/MJy07qNp
uKfAmOXYbfJvtj9nZ1Ww9e340LIb59yJDU7okBe0mbdQqnRkJOUXXBtysyWxHcfi
psHmAOU4OhzmNp2chbNOOgQcnWPO1hP/ZhejY1Irnw/3htEk64Ryyne9XZchLFHO
nMfyenqHHRcqogvP0M00YG+cFnJ3OUXFpZmn9+WJR48AVe2JI4Z3EO5wKDUGiK/n
S6/5YLRddG/THvPVsT+QY8pj5yKwEe7HxncC5O4k9Ve3q7AcTIQ=
=fQr3
-----END PGP SIGNATURE-----