Skip to contentSkip to conversation

man/: SYNOPSIS: Remove GNU forward-declarations of parameters for lengths of array parameters

PATCH NAK Alejandro Colomar 7 participants 29 messages Oct 22 – Oct 29 1 message not in the archive Man-pages: manual pages for Linux -- Sections 2, 3, 4, 5, and 7

lore
Share & tools
lore

Alejandro Colomar

author maintainer
#0 lore raw

PATCH 1/1 man/: SYNOPSIS: Remove GNU forward-declarations of parameters for lengths of array parameters

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Concerns that have been raised against using the GNU C syntax in the manual pages:

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Do we think programmers are unable to ever learn this new syntax? Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

I've cut the diff below to just a single page, because this is just for discussion; the actual diff is huge and uninteresting. It's just a removal of all uses of fwd decls of parameters in the manual pages.

Have a lovely day!
Alex

 man/man2/add_key.2                       |  2 +-
 man/man2/alloc_hugepages.2               |  2 +-
 man/man2/cacheflush.2                    |  2 +-
 man/man2/epoll_wait.2                    |  6 +++---
 man/man2/futex_waitv.2                   |  2 +-
 man/man2/get_mempolicy.2                 |  2 +-
 man/man2/getdents.2                      |  2 +-
 man/man2/getdomainname.2                 |  4 ++--
 man/man2/getrandom.2                     |  2 +-
 man/man2/getsockopt.2                    |  4 ++--
 man/man2/getunwind.2                     |  2 +-
 man/man2/getxattr.2                      |  6 +++---
 man/man2/init_module.2                   |  2 +-
 man/man2/listmount.2                     |  2 +-
 man/man2/lookup_dcookie.2                |  2 +-
 man/man2/madvise.2                       |  2 +-
 man/man2/mbind.2                         |  2 +-
 man/man2/mincore.2                       |  2 +-
 man/man2/mlock.2                         |  6 +++---
 man/man2/mmap.2                          |  4 ++--
 man/man2/modify_ldt.2                    |  2 +-
 man/man2/mprotect.2                      |  4 ++--
 man/man2/mremap.2                        |  2 +-
 man/man2/msgop.2                         |  4 ++--
 man/man2/msync.2                         |  2 +-
 man/man2/outb.2                          | 12 ++++++------
 man/man2/perfmonctl.2                    |  2 +-
 man/man2/pread.2                         |  4 ++--
 man/man2/query_module.2                  |  2 +-
 man/man2/read.2                          |  2 +-
 man/man2/readlink.2                      |  4 ++--
 man/man2/recv.2                          |  4 ++--
 man/man2/recvmmsg.2                      |  2 +-
 man/man2/remap_file_pages.2              |  2 +-
 man/man2/s390_pci_mmio_write.2           |  4 ++--
 man/man2/send.2                          |  4 ++--
 man/man2/sendmmsg.2                      |  2 +-
 man/man2/setxattr.2                      |  6 +++---
 man/man2/write.2                         |  2 +-
 man/man2const/KEYCTL_DESCRIBE.2const     |  2 +-
 man/man2const/KEYCTL_DH_COMPUTE.2const   |  2 +-
 man/man2const/KEYCTL_GET_SECURITY.2const |  2 +-
 man/man2const/KEYCTL_INSTANTIATE.2const  |  4 ++--
 man/man2const/KEYCTL_READ.2const         |  2 +-
 man/man2const/KEYCTL_UPDATE.2const       |  2 +-
 man/man2const/PR_GET_AUXV.2const         |  2 +-
 man/man3/aio_suspend.3                   |  2 +-
 man/man3/arc4random.3                    |  2 +-
 man/man3/backtrace.3                     |  6 +++---
 man/man3/bcmp.3                          |  2 +-
 man/man3/bcopy.3                         |  2 +-
 man/man3/bsearch.3                       |  2 +-
 man/man3/bzero.3                         |  2 +-
 man/man3/cfree.3                         |  4 ++--
 man/man3/confstr.3                       |  2 +-
 man/man3/des_crypt.3                     |  4 ++--
 man/man3/explicit_bzero.3                |  2 +-
 man/man3/fgets.3                         |  2 +-
 man/man3/fgetws.3                        |  2 +-
 man/man3/fmemopen.3                      |  2 +-
 man/man3/fread.3                         |  4 ++--
 man/man3/getcwd.3                        |  2 +-
 man/man3/getdirentries.3                 |  2 +-
 man/man3/getentropy.3                    |  2 +-
 man/man3/getgrent_r.3                    |  4 ++--
 man/man3/getgrnam.3                      |  4 ++--
 man/man3/gethostbyname.3                 | 10 +++++-----
 man/man3/getipnodebyname.3               |  2 +-
 man/man3/getlogin.3                      |  2 +-
 man/man3/getmntent.3                     |  2 +-
 man/man3/getnameinfo.3                   |  2 +-
 man/man3/getnetent_r.3                   |  6 +++---
 man/man3/getprotoent_r.3                 |  6 +++---
 man/man3/getpwent_r.3                    |  4 ++--
 man/man3/getpwnam.3                      |  4 ++--
 man/man3/getrpcent_r.3                   |  6 +++---
 man/man3/getservent_r.3                  |  6 +++---
 man/man3/getspnam.3                      |  8 ++++----
 man/man3/inet_net_pton.3                 |  4 ++--
 man/man3/inet_ntop.3                     |  2 +-
 man/man3/lio_listio.3                    |  2 +-
 man/man3/lsearch.3                       |  4 ++--
 man/man3/mblen.3                         |  2 +-
 man/man3/mbrlen.3                        |  2 +-
 man/man3/mbrtowc.3                       |  2 +-
 man/man3/mbsnrtowcs.3                    |  2 +-
 man/man3/mbsrtowcs.3                     |  2 +-
 man/man3/mbstowcs.3                      |  2 +-
 man/man3/mbtowc.3                        |  2 +-
 man/man3/memccpy.3                       |  2 +-
 man/man3/memchr.3                        |  2 +-
 man/man3/memcmp.3                        |  2 +-
 man/man3/memcpy.3                        |  2 +-
 man/man3/memfrob.3                       |  2 +-
 man/man3/memmem.3                        |  2 +-
 man/man3/memmove.3                       |  2 +-
 man/man3/mempcpy.3                       |  2 +-
 man/man3/memrchr.3                       |  2 +-
 man/man3/memset.3                        |  2 +-
 man/man3/memset_explicit.3               |  2 +-
 man/man3/mq_receive.3                    |  4 ++--
 man/man3/mq_send.3                       |  4 ++--
 man/man3/posix_madvise.3                 |  2 +-
 man/man3/pthread_attr_setstack.3         |  2 +-
 man/man3/pthread_setname_np.3            |  2 +-
 man/man3/ptsname.3                       |  2 +-
 man/man3/qsort.3                         |  4 ++--
 man/man3/random.3                        |  2 +-
 man/man3/regex.3                         |  2 +-
 man/man3/resolver.3                      | 24 ++++++++++++------------
 man/man3/setaliasent.3                   |  4 ++--
 man/man3/setbuf.3                        |  4 ++--
 man/man3/setnetgrent.3                   |  2 +-
 man/man3/snprintf.3                      |  4 ++--
 man/man3/stpncpy.3                       |  2 +-
 man/man3/strerror.3                      |  4 ++--
 man/man3/strfmon.3                       |  4 ++--
 man/man3/strfromd.3                      |  6 +++---
 man/man3/strftime.3                      |  4 ++--
 man/man3/strlcpy.3                       |  4 ++--
 man/man3/strncat.3                       |  2 +-
 man/man3/strncpy.3                       |  2 +-
 man/man3/strndup.3                       |  2 +-
 man/man3/strndupa.3                      |  2 +-
 man/man3/strxfrm.3                       |  2 +-
 man/man3/swab.3                          |  2 +-
 man/man3/ttyname.3                       |  2 +-
 man/man3/unlocked_stdio.3                |  8 ++++----
 man/man3/wcpncpy.3                       |  2 +-
 man/man3/wcsncat.3                       |  2 +-
 man/man3/wcsncpy.3                       |  2 +-
 man/man3/wcsnrtombs.3                    |  2 +-
 man/man3/wcsrtombs.3                     |  2 +-
 man/man3/wcstombs.3                      |  2 +-
 man/man3/wmemchr.3                       |  2 +-
 man/man3/wmemcmp.3                       |  2 +-
 man/man3/wmemcpy.3                       |  2 +-
 man/man3/wmemmove.3                      |  2 +-
 man/man3/wmempcpy.3                      |  2 +-
 man/man3/wmemset.3                       |  2 +-
 man/man3/wprintf.3                       |  4 ++--
 man/man7/man-pages.7                     |  2 +-
 man/man7/string_copying.7                | 16 ++++++++--------
 man/man9/strscpy.9                       |  2 +-
 144 files changed, 228 insertions(+), 228 deletions(-)
man/man2/add_key.2 +1 -1
index 19bc5d40ae76..908c63a10fbb 100644
@@ -14,7 +14,7 @@ .SH SYNOPSIS
 .nf
 .B #include <keyutils.h>
 .P
-.BR key_serial_t\~add_key( size_t\~size;
+.B key_serial_t\~add_key(
 .BI "                     const char *" type ", const char *" description ,
 .BI "                     const void " payload [ size "], size_t " size ,
 .BI "                     key_serial_t " keyring ");"
Signature · 1 line
2.55.0
#1 lore raw

Here are my 2 cents:

The one big advantage of the forward declaration syntax is, that if one does use it, one can get real benefits in security already today in the forms of compiler warnings and when using object-size sanitizer. In my opinion, it is real tragedy for the world that it is not more widely known and used (and that Clang does not want to implement it).

On the other hand, we have now a counter proposal from Apple which would make the simpler syntax with retrospective scoping work. I still do not like it, as the fully developed form would be a major pain for most C compiler to implement, but I guess a restricted version would be ok. Unfortunately, this syntax does not work today, so could be used in code only at some point in the future.

Still, my recommendation would be to wait until this issue is settled in WG14 and if Apple's proposal is accepted, use Apple's syntax in the man pages.

Martin

*) Also I hate giving in to bullies. (not talking about the Apple engineers)

Am Donnerstag, dem 22.10.2026 um 18:44 +0200 schrieb Alejandro Colomar:

Quoted 237 lines

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Concerns that have been raised against using the GNU C syntax in the manual pages:

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Do we think programmers are unable to ever learn this new syntax? Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

I've cut the diff below to just a single page, because this is just for discussion; the actual diff is huge and uninteresting. It's just a removal of all uses of fwd decls of parameters in the manual pages.

Have a lovely day!
Alex

 man/man2/add_key.2                       |  2 +-
 man/man2/alloc_hugepages.2               |  2 +-
 man/man2/cacheflush.2                    |  2 +-
 man/man2/epoll_wait.2                    |  6 +++---
 man/man2/futex_waitv.2                   |  2 +-
 man/man2/get_mempolicy.2                 |  2 +-
 man/man2/getdents.2                      |  2 +-
 man/man2/getdomainname.2                 |  4 ++--
 man/man2/getrandom.2                     |  2 +-
 man/man2/getsockopt.2                    |  4 ++--
 man/man2/getunwind.2                     |  2 +-
 man/man2/getxattr.2                      |  6 +++---
 man/man2/init_module.2                   |  2 +-
 man/man2/listmount.2                     |  2 +-
 man/man2/lookup_dcookie.2                |  2 +-
 man/man2/madvise.2                       |  2 +-
 man/man2/mbind.2                         |  2 +-
 man/man2/mincore.2                       |  2 +-
 man/man2/mlock.2                         |  6 +++---
 man/man2/mmap.2                          |  4 ++--
 man/man2/modify_ldt.2                    |  2 +-
 man/man2/mprotect.2                      |  4 ++--
 man/man2/mremap.2                        |  2 +-
 man/man2/msgop.2                         |  4 ++--
 man/man2/msync.2                         |  2 +-
 man/man2/outb.2                          | 12 ++++++------
 man/man2/perfmonctl.2                    |  2 +-
 man/man2/pread.2                         |  4 ++--
 man/man2/query_module.2                  |  2 +-
 man/man2/read.2                          |  2 +-
 man/man2/readlink.2                      |  4 ++--
 man/man2/recv.2                          |  4 ++--
 man/man2/recvmmsg.2                      |  2 +-
 man/man2/remap_file_pages.2              |  2 +-
 man/man2/s390_pci_mmio_write.2           |  4 ++--
 man/man2/send.2                          |  4 ++--
 man/man2/sendmmsg.2                      |  2 +-
 man/man2/setxattr.2                      |  6 +++---
 man/man2/write.2                         |  2 +-
 man/man2const/KEYCTL_DESCRIBE.2const     |  2 +-
 man/man2const/KEYCTL_DH_COMPUTE.2const   |  2 +-
 man/man2const/KEYCTL_GET_SECURITY.2const |  2 +-
 man/man2const/KEYCTL_INSTANTIATE.2const  |  4 ++--
 man/man2const/KEYCTL_READ.2const         |  2 +-
 man/man2const/KEYCTL_UPDATE.2const       |  2 +-
 man/man2const/PR_GET_AUXV.2const         |  2 +-
 man/man3/aio_suspend.3                   |  2 +-
 man/man3/arc4random.3                    |  2 +-
 man/man3/backtrace.3                     |  6 +++---
 man/man3/bcmp.3                          |  2 +-
 man/man3/bcopy.3                         |  2 +-
 man/man3/bsearch.3                       |  2 +-
 man/man3/bzero.3                         |  2 +-
 man/man3/cfree.3                         |  4 ++--
 man/man3/confstr.3                       |  2 +-
 man/man3/des_crypt.3                     |  4 ++--
 man/man3/explicit_bzero.3                |  2 +-
 man/man3/fgets.3                         |  2 +-
 man/man3/fgetws.3                        |  2 +-
 man/man3/fmemopen.3                      |  2 +-
 man/man3/fread.3                         |  4 ++--
 man/man3/getcwd.3                        |  2 +-
 man/man3/getdirentries.3                 |  2 +-
 man/man3/getentropy.3                    |  2 +-
 man/man3/getgrent_r.3                    |  4 ++--
 man/man3/getgrnam.3                      |  4 ++--
 man/man3/gethostbyname.3                 | 10 +++++-----
 man/man3/getipnodebyname.3               |  2 +-
 man/man3/getlogin.3                      |  2 +-
 man/man3/getmntent.3                     |  2 +-
 man/man3/getnameinfo.3                   |  2 +-
 man/man3/getnetent_r.3                   |  6 +++---
 man/man3/getprotoent_r.3                 |  6 +++---
 man/man3/getpwent_r.3                    |  4 ++--
 man/man3/getpwnam.3                      |  4 ++--
 man/man3/getrpcent_r.3                   |  6 +++---
 man/man3/getservent_r.3                  |  6 +++---
 man/man3/getspnam.3                      |  8 ++++----
 man/man3/inet_net_pton.3                 |  4 ++--
 man/man3/inet_ntop.3                     |  2 +-
 man/man3/lio_listio.3                    |  2 +-
 man/man3/lsearch.3                       |  4 ++--
 man/man3/mblen.3                         |  2 +-
 man/man3/mbrlen.3                        |  2 +-
 man/man3/mbrtowc.3                       |  2 +-
 man/man3/mbsnrtowcs.3                    |  2 +-
 man/man3/mbsrtowcs.3                     |  2 +-
 man/man3/mbstowcs.3                      |  2 +-
 man/man3/mbtowc.3                        |  2 +-
 man/man3/memccpy.3                       |  2 +-
 man/man3/memchr.3                        |  2 +-
 man/man3/memcmp.3                        |  2 +-
 man/man3/memcpy.3                        |  2 +-
 man/man3/memfrob.3                       |  2 +-
 man/man3/memmem.3                        |  2 +-
 man/man3/memmove.3                       |  2 +-
 man/man3/mempcpy.3                       |  2 +-
 man/man3/memrchr.3                       |  2 +-
 man/man3/memset.3                        |  2 +-
 man/man3/memset_explicit.3               |  2 +-
 man/man3/mq_receive.3                    |  4 ++--
 man/man3/mq_send.3                       |  4 ++--
 man/man3/posix_madvise.3                 |  2 +-
 man/man3/pthread_attr_setstack.3         |  2 +-
 man/man3/pthread_setname_np.3            |  2 +-
 man/man3/ptsname.3                       |  2 +-
 man/man3/qsort.3                         |  4 ++--
 man/man3/random.3                        |  2 +-
 man/man3/regex.3                         |  2 +-
 man/man3/resolver.3                      | 24 ++++++++++++------------
 man/man3/setaliasent.3                   |  4 ++--
 man/man3/setbuf.3                        |  4 ++--
 man/man3/setnetgrent.3                   |  2 +-
 man/man3/snprintf.3                      |  4 ++--
 man/man3/stpncpy.3                       |  2 +-
 man/man3/strerror.3                      |  4 ++--
 man/man3/strfmon.3                       |  4 ++--
 man/man3/strfromd.3                      |  6 +++---
 man/man3/strftime.3                      |  4 ++--
 man/man3/strlcpy.3                       |  4 ++--
 man/man3/strncat.3                       |  2 +-
 man/man3/strncpy.3                       |  2 +-
 man/man3/strndup.3                       |  2 +-
 man/man3/strndupa.3                      |  2 +-
 man/man3/strxfrm.3                       |  2 +-
 man/man3/swab.3                          |  2 +-
 man/man3/ttyname.3                       |  2 +-
 man/man3/unlocked_stdio.3                |  8 ++++----
 man/man3/wcpncpy.3                       |  2 +-
 man/man3/wcsncat.3                       |  2 +-
 man/man3/wcsncpy.3                       |  2 +-
 man/man3/wcsnrtombs.3                    |  2 +-
 man/man3/wcsrtombs.3                     |  2 +-
 man/man3/wcstombs.3                      |  2 +-
 man/man3/wmemchr.3                       |  2 +-
 man/man3/wmemcmp.3                       |  2 +-
 man/man3/wmemcpy.3                       |  2 +-
 man/man3/wmemmove.3                      |  2 +-
 man/man3/wmempcpy.3                      |  2 +-
 man/man3/wmemset.3                       |  2 +-
 man/man3/wprintf.3                       |  4 ++--
 man/man7/man-pages.7                     |  2 +-
 man/man7/string_copying.7                | 16 ++++++++--------
 man/man9/strscpy.9                       |  2 +-
 144 files changed, 228 insertions(+), 228 deletions(-)
man/man2/add_key.2 +1 -1
index 19bc5d40ae76..908c63a10fbb 100644
@@ -14,7 +14,7 @@ .SH SYNOPSIS
 .nf
 .B #include <keyutils.h>
 .P
-.BR key_serial_t\~add_key( size_t\~size;
+.B key_serial_t\~add_key(
 .BI "                     const char *" type ", const char *" description ,
 .BI "                     const void " payload [ size "], size_t " size ,
 .BI "                     key_serial_t " keyring ");"
#2 lore raw

Hi Martin,

Quoted 4 lines

Date: 2026-10-22 19:11:41+0200
From: Martin Uecker <[email protected]>

Quoted 8 lines

Here are my 2 cents:

The one big advantage of the forward declaration syntax is, that if one does use it, one can get real benefits in security already today in the forms of compiler warnings and when using object-size sanitizer. In my opinion, it is real tragedy for the world that it is not more widely known and used (and that Clang does not want to implement it).

In this case, we're only concerned about documentation. I fully agree that in source code, forward declarations are the way to go. What we're trying to decide here is whether the same is true in documentation or whether there are reasons to do something else.

Quoted 3 lines

On the other hand, we have now a counter proposal from Apple which would make the simpler syntax with retrospective scoping work.

I'm not sure about that. There are concerns about it changing the meaning of existing code if the constraint is not added (and if it is added, of breaking code at a distance). That proposal is not yet ready.

And indeed, some of those concerns are related to the concerns I have in manual pages: if users take this syntax and start using it, they might have serious issues in their code.

Quoted 9 lines

I still do not like it, as the fully developed form
would be a major pain for most C compiler to implement, but I guess a restricted version would be ok. Unfortunately, this syntax does not work today, so could be used in code only at some point in the future.

Still, my recommendation would be to wait until this issue is settled in WG14 and if Apple's proposal is accepted, use Apple's syntax in the man pages.

Yes, if Apple's syntax is accepted, we should probably change the documentation. Michael thinks there are reasons to move earlier, though. I'm not entirely convinced about his position.

I see that he's worried about users hitting a spike of confusion reading this GNU C syntax in manual pages. I'm not convinced that this will last for long, though.

Have a lovely night!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#3 lore raw

Hi Martin,

Quoted 3 lines

Date: 2026-10-22 22:01:17+0200
From: Alejandro Colomar <[email protected]>

[...]

Quoted 5 lines
Quoted 5 lines

I still do not like it, as the fully developed form
would be a major pain for most C compiler to implement, but I guess a restricted version would be ok. Unfortunately, this syntax does not work today, so could be used in code only at some point in the future.

This syntax is useless for us. We have several functions that need to write expressions in array lengths, and not just identifiers. We even dereference some pointer types (see getsockopt(2), lfind(3), and lsearch(3)).

It's also useless for me as a programmer, since I need to do non-trivial stuff in my array sizes. Whatever we add to the language should work for arbitrary types and expressions.

Here's a list of functions we have documented and which need a non-restricted feature.

     long get_mempolicy(unsigned long maxnode;
                        int *mode,
                        unsigned long nodemask[(maxnode + ULONG_WIDTH - 1)
                                               / ULONG_WIDTH],
                        unsigned long maxnode, void *addr,
                        unsigned long flags);

     char *inet_net_ntop(int bits, size_t psize;
                       int af,
                       const void netp[(bits - CHAR_BIT + 1) / CHAR_BIT],
                       int bits,
                       char pres[psize], size_t psize);

     int getsockopt(socklen_t *restrict optlen;
                    int sockfd, int level, int optname,
                    void optval[_Nullable restrict *optlen],
                    socklen_t *restrict optlen);

     void *bsearch(size_t n, size_t size;
                   const void key[size], const void base[size * n],
                   size_t n, size_t size,
                   typeof(int (const void [size], const void [size]))
                       *compar);

     /* In SCO OpenServer */
     void cfree(unsigned int n, unsigned int size;
                char ptr[size * n], unsigned int n, unsigned int size);

     /* In Solaris watchmalloc.so.1 */
     void cfree(size_t n, size_t size;
                void ptr[size * n], size_t n, size_t size);

     size_t fread(size_t size, size_t n;
                  void ptr[restrict size * n],
                  size_t size, size_t n,
                  FILE *restrict stream);
     size_t fwrite(size_t size, size_t n;
                  const void ptr[restrict size * n],
                  size_t size, size_t n,
                  FILE *restrict stream);

     size_t fread_unlocked(size_t size, size_t n;
                           void ptr[restrict size * n],
                           size_t size, size_t n,
                           FILE *restrict stream);
     size_t fwrite_unlocked(size_t size, size_t n;
                           const void ptr[restrict size * n],
                           size_t size, size_t n,
                           FILE *restrict stream);

     void *lfind(size_t *n, size_t size;
                 const void key[size],
                 const void base[*n * size],
                 size_t *n, size_t size,
                 typeof(int (const void [size], const void [size]))
                     *compar);
     void *lsearch(size_t *n, size_t size;
                 const void key[size],
                 void base[*n * size],
                 size_t *n, size_t size,
                 typeof(int (const void [size], const void [size]))
                     *compar);

I might have missed some other functions; I think this isn't exhaustive.

Cheers,
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#4 lore raw
In reply to Alejandro Colomar

Hi Alejandro,

Quoted 7 lines · Alejandro Colomar

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

Why did Michael ask you to do this? I think there is some context missing.

Quoted 7 lines

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

Quoted 7 lines

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

I think it is reasonable to assume that anything which resembles C source code will at some point be copied and pasted into a C program.

Quoted 10 lines

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

I agree with this concern in general, although I don.t quite see how it applies in the specific case of the functions that you are documenting. (Unless you are expecting those functions to be declared inside a function definition instead of by including the appropriate header - in which case the hazard already exists.)

I guess you are really worried about people copying the style rather than the content.

Quoted 2 lines

Concerns that have been raised against using the GNU C syntax in the manual pages:

Parameter forward declarations are also supported by SDCC, albeit with more limitations. I would be interested to see whether your usage falls into the category of usage that would be supported by SDCC or not.

Quoted 7 lines

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

Can we still call it innovative, many decades after its invention?

Quoted 21 lines

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Quoted 1 line

Do we think programmers are unable to ever learn this new syntax?

No, because programmers are clever and it is relatively simple. I wrote a paper that explored the drawbacks of the syntax and recommended standardising a subset (which I assume is what you are using). With a free hand, I might have been tempted to go further, but it is well-established and impossible to redefine.

I still have not yet seen a proposal for anything easier to learn (that does not rely on forward-parsing).

Quoted 11 lines

Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

I've cut the diff below to just a single page, because this is just for discussion; the actual diff is huge and uninteresting. It's just a removal of all uses of fwd decls of parameters in the manual pages.

I don't agree with the change, but the fact that there are no useful ISO C semantics for parameters of pointer type declared using array syntax weakens my concern. (If the parameters actually had variably-modified types then the change would be far worse, from my point of view, because the parameter types would no longer be expressed correctly.)

You know your customers better than I do. If they expect GNU C semantics, then it seems reasonable to document those semantics using GNU C syntax.

Christopher

A message that is not in the archive (sent off-list or not yet received): CAEHU8x9jET+vcrR0NpDz6t57egQG8TZqx5XtV52srH9ZRvdxSQ@mail.gmail.com

#6 lore raw

Hi Chris,

Quoted 9 lines

Date: 2026-10-22 18:29:31+0100
From: Christopher Bazley <[email protected]>

Hi Alejandro,

Quoted 2 lines · Alejandro Colomar

From: Alejandro Colomar <[email protected]>

Quoted 8 lines
Quoted 5 lines

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

Why did Michael ask you to do this? I think there is some context missing.

We have dozens of users having reported a bug in the documentation, as they believe the forward declarations are an accident/typo. Clearly, most readers don't know this syntax, and think it's some mistake. I've been dealing with them nicely explaining them the existance and validity of the syntax, and they tend to understand it quickly.

Quoted 16 lines
Quoted 7 lines

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

Quoted 7 lines

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

Quoted 2 lines

I think it is reasonable to assume that anything which resembles C source code will at some point be copied and pasted into a C program.

Indeed.

Quoted 11 lines
Quoted 10 lines

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Quoted 8 lines

I agree with this concern in general, although I don’t quite see how it applies in the specific case of the functions that you are documenting. (Unless you are expecting those functions to be declared inside a function definition instead of by including the appropriate header — in which case the hazard already exists.)

I guess you are really worried about people copying the style rather than the content.

Indeed, my worry is not that people will copy these declarations verbatim, but that they'll think it is valid, and write similar code for their own declarations.

Quoted 7 lines
Quoted 2 lines

Concerns that have been raised against using the GNU C syntax in the manual pages:

Parameter forward declarations are also supported by SDCC, albeit with more limitations. I would be interested to see whether your usage falls into the category of usage that would be supported by SDCC or not.

Our users are mainly interested in POSIX compilers. That is more or less GCC, Clang, TCC, PCC, and maybe something like Intel or IBM compilers. The main two are GCC and Clang.

Quoted 9 lines
Quoted 7 lines

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

Can we still call it innovative, many decades after its invention?

Maybe I should have used the term 'novel' rather than innovative. While it's clearly not new, being decades old, it is certainly a novelty to the programmers that have never seen it before.

Quoted 20 lines
Quoted 20 lines

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Quoted 6 lines
Quoted 1 line

Do we think programmers are unable to ever learn this new syntax?

No, because programmers are clever and it is relatively simple. I wrote a paper that explored the drawbacks of the syntax and recommended standardising a subset (which I assume is what you are using).

[...]

Agree.

Quoted 2 lines

I still have not yet seen a proposal for anything easier to learn (that does not rely on forward-parsing).

Agree. But indeed the counter-proposal that I've received is to use retroactive scoping in the manual pages.

Quoted 7 lines
Quoted 7 lines

Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

Quoted 6 lines
Quoted 4 lines

I've cut the diff below to just a single page, because this is just for discussion; the actual diff is huge and uninteresting. It's just a removal of all uses of fwd decls of parameters in the manual pages.

I don’t agree with the change,

Thanks!

Quoted 5 lines

but the fact that there are no useful ISO C
semantics for parameters of pointer type declared using array syntax weakens my concern. (If the parameters actually had variably-modified types then the change would be far worse, from my point of view, because the parameter types would no longer be expressed correctly.)

We don't care about what ISO C says. GNU C clearly assigns semantics to array parameters, and that means we have to be careful about them here. If this can result in sanitizers breaking code, it is something to be careful about. Moreover, I hope ISO C eventually catches up.

Quoted 2 lines

You know your customers better than I do. If they expect GNU C semantics, then it seems reasonable to document those semantics using GNU C syntax.

Agree.

Have a lovely night!
Alex

P.S.: I see two emails from you; are they the same? One has a smaller recipient list, but the contents seem visually the same.

Quoted 2 lines

Christopher

Signature · 1 line
<https://www.alejandro-colomar.es>
#7 lore raw

Hello,

Quoted 18 lines · Alejandro Colomar

Hi Chris,

Quoted 15 lines

Date: 2026-10-22 18:29:31+0100
From: Christopher Bazley <[email protected]>

Hi Alejandro,

Quoted 7 lines · Alejandro Colomar

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

Quoted 8 lines
Quoted 1 line

Why did Michael ask you to do this? I think there is some context missing.

We have dozens of users having reported a bug in the documentation, as they believe the forward declarations are an accident/typo. Clearly, most readers don't know this syntax, and think it's some mistake. I've been dealing with them nicely explaining them the existance and validity of the syntax, and they tend to understand it quickly.

I spoke with Alex a couple of times about this in person. Via man7.org I have received numerous bug reports about this syntax. It is nonstandard, people are not at all familiar with it, and they will potentially misinterpret it in ways that will lead them to make errors when writing code.

Quoted 62 lines
Quoted 9 lines
Quoted 9 lines

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

However, I still have doubts, because this change comes with risks:

Quoted 7 lines
Quoted 4 lines

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

I think it is reasonable to assume that anything which resembles C source code will at some point be copied and pasted into a C program.

Indeed.

Quoted 11 lines
Quoted 10 lines

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Quoted 8 lines

I agree with this concern in general, although I don’t quite see how it applies in the specific case of the functions that you are documenting. (Unless you are expecting those functions to be declared inside a function definition instead of by including the appropriate header — in which case the hazard already exists.)

I guess you are really worried about people copying the style rather than the content.

Indeed, my worry is not that people will copy these declarations verbatim, but that they'll think it is valid, and write similar code for their own declarations.

Quoted 7 lines
Quoted 2 lines

Concerns that have been raised against using the GNU C syntax in the manual pages:

Parameter forward declarations are also supported by SDCC, albeit with more limitations. I would be interested to see whether your usage falls into the category of usage that would be supported by SDCC or not.

Our users are mainly interested in POSIX compilers. That is more or less GCC, Clang, TCC, PCC, and maybe something like Intel or IBM compilers. The main two are GCC and Clang.

Quoted 7 lines
Quoted 6 lines

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

Quoted 25 lines
Quoted 1 line

Can we still call it innovative, many decades after its invention?

Maybe I should have used the term 'novel' rather than innovative. While it's clearly not new, being decades old, it is certainly a novelty to the programmers that have never seen it before.

Quoted 19 lines
Quoted 19 lines

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

It is not enough to say that the compiler will warn people about the incorrect arguments. I have seen people do the strangest (incorrect) things to "fix" warnings and errors they get from the compiler.

Quoted 5 lines
Quoted 5 lines
Quoted 1 line

Do we think programmers are unable to ever learn this new syntax?

No, because programmers are clever and it is relatively simple. I wrote a paper that explored the drawbacks of the syntax and recommended standardising a subset (which I assume is what you are using).

I have talked with programmers with *decades* of C programming experience who are unfamiliar with this syntax. Indeed, when I first got bug reports about, I also was unfamiliar with it, and thought it was just a bug in the manual page.

Quoted 32 lines

[...]

Agree.

Quoted 2 lines

I still have not yet seen a proposal for anything easier to learn (that does not rely on forward-parsing).

Agree. But indeed the counter-proposal that I've received is to use retroactive scoping in the manual pages.

Quoted 7 lines
Quoted 7 lines

Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

Quoted 6 lines
Quoted 4 lines

I've cut the diff below to just a single page, because this is just for discussion; the actual diff is huge and uninteresting. It's just a removal of all uses of fwd decls of parameters in the manual pages.

I don’t agree with the change,

Thanks!

Quoted 5 lines

but the fact that there are no useful ISO C
semantics for parameters of pointer type declared using array syntax weakens my concern. (If the parameters actually had variably-modified types then the change would be far worse, from my point of view, because the parameter types would no longer be expressed correctly.)

Quoted 7 lines

We don't care about what ISO C says. GNU C clearly assigns semantics to array parameters, and that means we have to be careful about them here. If this can result in sanitizers breaking code, it is something to be careful about. Moreover, I hope ISO C eventually catches up.

Quoted 2 lines

You know your customers better than I do. If they expect GNU C semantics, then it seems reasonable to document those semantics using GNU C syntax.

The customers clearly do NOT expect GNU C syntax.

My preference would be the earlier status quo: standard C syntax, with an explanation in the manual page text that explains the relationship between the arguments. The current proposed patch is a compromise: it has the disadvantage of being nonstandard C, but does at least clearly hint at the relationship between the arguments.

Thanks,

Michael

Nacked-by Nacked-by Martin Uecker Nacked-by Christopher Bazley Acked-by Mark Harris Acked-by Michael Kerrisk NAK
#8 lore raw

Hi Michael,

Quoted 3 lines

Date: 2026-10-23 08:53:22-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

[...]

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Quoted 3 lines

It is not enough to say that the compiler will warn people about the incorrect arguments. I have seen people do the strangest (incorrect) things to "fix" warnings and errors they get from the compiler.

Hmmmm.

Quoted 10 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line

Do we think programmers are unable to ever learn this new syntax?

No, because programmers are clever and it is relatively simple. I wrote a paper that explored the drawbacks of the syntax and recommended standardising a subset (which I assume is what you are using).

I have talked with programmers with *decades* of C programming experience who are unfamiliar with this syntax. Indeed, when I first got bug reports about, I also was unfamiliar with it, and thought it was just a bug in the manual page.

I agree with this. Very very few people knew about this feature until it started being discussed recently, and still very few people know about it. I only knew about it around 3 years ago or so.

What I wonder is whether people can get familiar with it in one or two years of exposure to it.

One way to test that would be to wait until Fedora contains this syntax at least in one release, to get it exposed to more programmers. So far, Fedora remains in man-pages-6.13, and the first version with this syntax was 6.14 (1.5 years ago). Carlos is working on getting the packaging updated.

There are risks in both alternatives: if we have the GNU C syntax, there's a risk with programmers workarounding compiler errors in weird ways. If we have the invalid syntax without forward declarations, the risk is in people using that syntax in their own code and triggering UB or least logic errors. The latter risk is a persistent one; there's no expectation that the risk will ever decrease. The former risk (that of using GNU C syntax), I expect the risk to be temporary, until programmers get accustomed to this syntax.

I'd accept adding this patch if there's an overwhelming majority that I'm being a bit paranoid about the risk of using invalid syntax, and that I'm understating the risk of using GNU C syntax.

I'd like to hear explicit comments from Carlos, Sam, and Joseph before taking a decision. But if they seem to agree with that point of view, I'd take the patch, even with my own NAK (I'm willing to document both ACKs and NAKs).

I've now also CCed a few more people, since I'm also curious about their opinions.

So far, the patch would have:

(those ACKs and NAKs transformed from messages saying they want or don't
want the patch.)

(Before anyone comments the possibility of going back to simple pointer
syntax, that's certainly not coming back. The information about array lengths is fundamental in the SYNOPSIS.)

[...]

Quoted 1 line

My preference would be the earlier status quo: standard C syntax, with

As a reminder, it was never standard C syntax. We have always had non-standard exceptions in prototypes, such as open(2) having two declarations (as if C++ overload syntax). It was ISO C syntax regarding array parameters, though: we used pointer syntax. But as said above, I really disliked having that information only in the DESCRIPTION.

Quoted 4 lines

an explanation in the manual page text that explains the relationship between the arguments. The current proposed patch is a compromise: it has the disadvantage of being nonstandard C, but does at least clearly hint at the relationship between the arguments.

Agree.

Have a lovely day!
Alex

Quoted 3 lines

Thanks,

Michael

Signature · 1 line
<https://www.alejandro-colomar.es>
#9 lore raw
In reply to Michael Kerrisk (man7.org)

Hi Michael,

Quoted 10 lines · On Fri, Oct 23, 2026 at 12:54 PM Michael Kerrisk (man7.org) <[email protected]>…

Hello,

Quoted 5 lines · Alejandro Colomar

Hi Chris,

Quoted 2 lines

Date: 2026-10-22 18:29:31+0100
From: Christopher Bazley <[email protected]>

...

Quoted 10 lines
Quoted 2 lines
Quoted 2 lines

You know your customers better than I do. If they expect GNU C semantics, then it seems reasonable to document those semantics using GNU C syntax.

The customers clearly do NOT expect GNU C syntax.

My preference would be the earlier status quo: standard C syntax, with an explanation in the manual page text that explains the relationship between the arguments. The current proposed patch is a compromise: it has the disadvantage of being nonstandard C, but does at least clearly hint at the relationship between the arguments.

I am curious to know what your attitude would be to lesser-known standard C syntax.

For example:

int pipe(int pipefd[static 2]);

is standard C syntax, and requires the caller to pass a pointer to the first of at least 2 array elements, unlike

int pipe(int pipefd[2]);

which has the same semantics in ISO C as

int pipe(int *pipefd);

Thanks,
Christopher

#10 lore raw

Hello Christopher

Quoted 34 lines · Christopher Bazley

Hi Michael,

Quoted 10 lines · On Fri, Oct 23, 2026 at 12:54 PM Michael Kerrisk (man7.org) <[email protected]>…

Hello,

Quoted 5 lines · Alejandro Colomar

Hi Chris,

Quoted 2 lines

Date: 2026-10-22 18:29:31+0100
From: Christopher Bazley <[email protected]>

...

Quoted 10 lines
Quoted 2 lines
Quoted 2 lines

You know your customers better than I do. If they expect GNU C semantics, then it seems reasonable to document those semantics using GNU C syntax.

The customers clearly do NOT expect GNU C syntax.

My preference would be the earlier status quo: standard C syntax, with an explanation in the manual page text that explains the relationship between the arguments. The current proposed patch is a compromise: it has the disadvantage of being nonstandard C, but does at least clearly hint at the relationship between the arguments.

I am curious to know what your attitude would be to lesser-known standard C syntax.

For example:

int pipe(int pipefd[static 2]);comprehensible

Quoted 8 lines

is standard C syntax, and requires the caller to pass a pointer to the first of at least 2 array elements, unlike

int pipe(int pipefd[2]);

which has the same semantics in ISO C as

int pipe(int *pipefd);

I was unfamiliar with that syntax, but at least in context it is immediately understandable, and I so I don't dislike it, And it is standard C. I really don't think Linux man-pages is the place to introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

Cheers,

Michael

Signature · 3 lines
Michael Kerrisk, man7.org Training and Consulting
[email protected], http://man7.org/training/
"The Linux Programming Interface" -- http://man7.org/tlpi/
#11 lore raw

Hi Michael,

Quoted 5 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

Hello Christopher

[...]

Quoted 10 lines
Quoted 10 lines

I am curious to know what your attitude would be to lesser-known standard C syntax.

For example:

int pipe(int pipefd[static 2]);comprehensible

is standard C syntax, and requires the caller to pass a pointer to the first of at least 2 array elements, unlike

Quoted 8 lines
Quoted 5 lines

int pipe(int pipefd[2]);

which has the same semantics in ISO C as

int pipe(int *pipefd);

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

- [2] means (in GNU C, and in common understanding, but not acknowledged by ISO C) that:

- The caller must supply at least 2 elements.
- The callee must not access past those 2 elements.

- [static 2] means (in ISO C, but ignored by GCC):

- The caller must supply at least 2 elements.
- The callee is free to access past those 2 elements!! (of course, if there aren't any elements after that, it's UB)
- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

Quoted 2 lines

and I so I don't dislike it, And it is
standard C.

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

People clearly don't understand [static 2] either.

Have a lovely day!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#12 lore raw
Quoted 37 lines · Alejandro Colomar

Hi Michael,

Quoted 5 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

Hello Christopher

[...]

Quoted 10 lines
Quoted 10 lines

I am curious to know what your attitude would be to lesser-known standard C syntax.

For example:

int pipe(int pipefd[static 2]);comprehensible

is standard C syntax, and requires the caller to pass a pointer to the first of at least 2 array elements, unlike

Quoted 8 lines
Quoted 5 lines

int pipe(int pipefd[2]);

which has the same semantics in ISO C as

int pipe(int *pipefd);

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

(quoted diff) +0 -1
-  [2] means (in GNU C, and in common understanding, but not

Review

Quoted 10 lines
(quoted diff) +0 -1
   acknowledged by ISO C) that:
 
        -  The caller must supply at least 2 elements.
        -  The callee must not access past those 2 elements.
 
-  [static 2] means (in ISO C, but ignored by GCC):
 
        -  The caller must supply at least 2 elements.
        -  The callee is free to access past those 2 elements!!
           (of course, if there aren't any elements after that, it's UB)

You can, of course, document for the benefit of callers that a function does not read past the static array bound.

Quoted 5 lines

- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

Of the compilers I have tried, only SDCC currently warns about access past the specified length, and it does so regardless of whether the length is specified using 'static' or not:

int foo(int a[static 2])
{
return a[2]; // warning from SDCC
}

int bar(int a[2])
{
return a[2]; // warning from SDCC
}

https://godbolt.org/z/zf3nPerdn

(I'm tempted to report the warning produced in the 'static' case as a bug, but in reality no warning is a conformance issue, so that characterisation is subjective.)

Quoted 9 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

Quoted 2 lines

and I so I don't dislike it, And it is
standard C.

Quoted 8 lines

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

People clearly don't understand [static 2] either.

My understanding is that [static 2] was introduced to facilitate cases like the following, where an implementation could theoretically generate code that reads the first four elements together, but would have to load subsequent elements individually:

int len(int s[static 4])
{
int i = 0;
do

  {
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;

} while (1);
return i;
}
https://godbolt.org/z/ra6zh7Ej3

I have never yet managed to persuade a compiler to optimise such code differently from a version without 'static 4', and I struggle to believe it would ever be beneficial to do so. The more obvious case, where an array bound such as [static 4 * n] potentially enables speculative loads when unrolling a loop with an early exit, does not need the compiler to be permitted to generate code that reads beyond the passed-in array bound.

Christopher

#13 lore raw

Hi Chris,

Quoted 9 lines

Date: 2026-10-24 17:16:19+0100
From: Christopher Bazley <[email protected]>

Quoted 4 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

[...]

Quoted 17 lines
Quoted 17 lines
Quoted 2 lines

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

- [2] means (in GNU C, and in common understanding, but not acknowledged by ISO C) that:

- The caller must supply at least 2 elements.
- The callee must not access past those 2 elements.

Quoted 8 lines
Quoted 5 lines

- [static 2] means (in ISO C, but ignored by GCC):

- The caller must supply at least 2 elements.
- The callee is free to access past those 2 elements!! (of course, if there aren't any elements after that, it's UB)

You can, of course, document for the benefit of callers that a function does not read past the static array bound.

Comments are bound to be wrong in the future, often near future. It's better to not use the feature, because it's not necessary. Just use [2], and ask your compiler to be better than what ISO C requires.

Quoted 15 lines
Quoted 5 lines

- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

Of the compilers I have tried, only SDCC currently warns about access past the specified length, and it does so regardless of whether the length is specified using 'static' or not:

int foo(int a[static 2])
{
return a[2]; // warning from SDCC
}

Quoted 6 lines

int bar(int a[2])
{
return a[2]; // warning from SDCC
}

https://godbolt.org/z/zf3nPerdn

GCC has some partial diagnostics for out-of-bounds accesses.

	alx@debian:~/tmp$ cat oob.c 
	void five(int a[5]);
	void four(int a[4])
	{
		five(a);
	}
	void three(int a[static 3])
	{
		five(a);
	}
	alx@debian:~/tmp$ gcc -Wall -Wextra -S oob.c 
	oob.c: In function ‘four’:
	oob.c:4:9: warning: ‘five’ accessing 20 bytes in a region of size 16 [-Wstringop-overflow=]
	    4 |         five(a);
	      |         ^~~~~~~
	oob.c:4:9: note: referencing argument 1 of type ‘int[5]’
	oob.c:1:6: note: in a call to function ‘five’
	    1 | void five(int a[5]);
	      |      ^~~~
	oob.c: In function ‘three’:
	oob.c:8:9: warning: ‘five’ accessing 20 bytes in a region of size 12 [-Wstringop-overflow=]
	    8 |         five(a);
	      |         ^~~~~~~
	oob.c:8:9: note: referencing argument 1 of type ‘int[5]’
	oob.c:1:6: note: in a call to function ‘five’
	    1 | void five(int a[5]);
	      |      ^~~~

I wish it diagnosed as much as SDCC, but not yet. :)

Quoted 3 lines

(I'm tempted to report the warning produced in the 'static' case as a bug, but in reality no warning is a conformance issue, so that characterisation is subjective.)

Clearly, not a bug, but rather QoI. ISO C has a bug.

Quoted 16 lines
Quoted 16 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

Quoted 2 lines

and I so I don't dislike it, And it is
standard C.

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

Quoted 6 lines
Quoted 1 line

People clearly don't understand [static 2] either.

My understanding is that [static 2] was introduced to facilitate cases like the following, where an implementation could theoretically generate code that reads the first four elements together, but would have to load subsequent elements individually:

My understanding is tthe same.

Quoted 15 lines

int len(int s[static 4])
{
int i = 0;
do

  {
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;

} while (1);
return i;
}
https://godbolt.org/z/ra6zh7Ej3

Quoted 7 lines

I have never yet managed to persuade a compiler to optimise such code differently from a version without 'static 4', and I struggle to believe it would ever be beneficial to do so. The more obvious case, where an array bound such as [static 4 * n] potentially enables speculative loads when unrolling a loop with an early exit, does not need the compiler to be permitted to generate code that reads beyond the passed-in array bound.

And indeed, I agree. This seems a case of bad design, wishing that it might eventually be optimizable, while it wasn't and still isn't, and also while ignoring the safety issues caused by it. We should just pretend that the feature doesn't exist.

Cheers,
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#14 lore raw

Am Samstag, dem 24.10.2026 um 18:37 +0200 schrieb Alejandro Colomar:

Quoted 35 lines

Hi Chris,

Quoted 9 lines

Date: 2026-10-24 17:16:19+0100
From: Christopher Bazley <[email protected]>

Quoted 4 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

[...]

Quoted 23 lines
Quoted 22 lines
Quoted 2 lines

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

(quoted diff) +0 -2
-  [2] means (in GNU C, and in common understanding, but not
   acknowledged by ISO C) that:
 
        -  The caller must supply at least 2 elements.
        -  The callee must not access past those 2 elements.
 
-  [static 2] means (in ISO C, but ignored by GCC):
 
        -  The caller must supply at least 2 elements.
        -  The callee is free to access past those 2 elements!!
           (of course, if there aren't any elements after that, it's UB)
Quoted 6 lines
Quoted 2 lines

You can, of course, document for the benefit of callers that a function does not read past the static array bound.

Comments are bound to be wrong in the future, often near future. It's better to not use the feature, because it's not necessary. Just use [2], and ask your compiler to be better than what ISO C requires.

[static ...] implies that the pointer is not null, this is a standard compliant way to express this (it implies a bit more as nonnull, but compilers do not support more). Still, it gives you a real practical security benefit.

void foo(int a[static 3]);
void bar()

{
	foo(nullptr);
}

<source>: In function 'bar':
<source>:5:9: warning: argument 1 null where non-null expected [-Wnonnull]

    5 |         foo(nullptr);
      |         ^~~
<source>:2:6: note: in a call to function 'foo' declared 'nonnull'
    2 | void foo(int a[static 3]);

https://godbolt.org/z/fa4YsWq63

Quoted 16 lines
Quoted 16 lines
Quoted 6 lines

- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

Of the compilers I have tried, only SDCC currently warns about access past the specified length, and it does so regardless of whether the length is specified using 'static' or not:

int foo(int a[static 2])
{
return a[2]; // warning from SDCC
}

Quoted 9 lines
Quoted 6 lines

int bar(int a[2])
{
return a[2]; // warning from SDCC
}

https://godbolt.org/z/zf3nPerdn

GCC has some partial diagnostics for out-of-bounds accesses.

... (example) ...

Quoted 2 lines

I wish it diagnosed as much as SDCC, but not yet. :)

The more relevant example is this:

void foo(int n; int a[n], int n);
void bar(int a[4])

{
	foo(a, 5);
}

<source>: In function 'bar':
<source>:5:9: warning: 'foo' accessing 20 bytes in a region of size 16 [-Wstringop-overflow=]

    5 |         foo(a, 5);
      |         ^~~~~~~~~
<source>:5:9: note: referencing argument 1 of type 'int[]'
<source>:2:6: note: in a call to function 'foo'
    2 | void foo(int n; int a[n], int n);

https://godbolt.org/z/hhGYTej49

This has a very real and obvious security benefit.

Quoted 7 lines

...

Quoted 5 lines
Quoted 5 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

It was not a mistake, it was a different set of priorities We now have more users and people interested in safety in the committee, but powers could easily shift back.

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

and I so I don't dislike it, And it is
standard C.

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

Quoted 31 lines
Quoted 6 lines
Quoted 1 line

People clearly don't understand [static 2] either.

My understanding is that [static 2] was introduced to facilitate cases like the following, where an implementation could theoretically generate code that reads the first four elements together, but would have to load subsequent elements individually:

My understanding is tthe same.

Quoted 22 lines

int len(int s[static 4])
{
int i = 0;
do

  {
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;

} while (1);
return i;
}
https://godbolt.org/z/ra6zh7Ej3

I have never yet managed to persuade a compiler to optimise such code differently from a version without 'static 4', and I struggle to believe it would ever be beneficial to do so. The more obvious case, where an array bound such as [static 4 * n] potentially enables speculative loads when unrolling a loop with an early exit, does not need the compiler to be permitted to generate code that reads beyond the passed-in array bound.

I think the idea of limiting access to the rest of the array did not occur to them, as this feature was only for optimization.

Quoted 5 lines

And indeed, I agree. This seems a case of bad design, wishing that it might eventually be optimizable, while it wasn't and still isn't, and also while ignoring the safety issues caused by it. We should just pretend that the feature doesn't exist.

It would potentially be useful for optimization, but I think there was a chicken-and-egg problem. Nobody used [static] and then people worked on instead optimizations more useful for existing code, so [static] also never become useful for programmers.

Much later it become useful to express non-null pointers.

Martin

Quoted 4 lines

Cheers,
Alex

#15 lore raw

Hi Martin,

Quoted 42 lines

Date: 2026-10-24 20:11:10+0200
From: Martin Uecker <[email protected]>

Am Samstag, dem 24.10.2026 um 18:37 +0200 schrieb Alejandro Colomar:

Quoted 38 lines

Hi Chris,

Quoted 9 lines

Date: 2026-10-24 17:16:19+0100
From: Christopher Bazley <[email protected]>

Quoted 4 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

[...]

Quoted 25 lines
Quoted 12 lines
Quoted 2 lines

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

(quoted diff) +0 -1
-  [2] means (in GNU C, and in common understanding, but not

Review

Quoted 10 lines
(quoted diff) +0 -1
   acknowledged by ISO C) that:
 
        -  The caller must supply at least 2 elements.
        -  The callee must not access past those 2 elements.
 
-  [static 2] means (in ISO C, but ignored by GCC):
 
        -  The caller must supply at least 2 elements.
        -  The callee is free to access past those 2 elements!!
           (of course, if there aren't any elements after that, it's UB)

You can, of course, document for the benefit of callers that a function does not read past the static array bound.

Quoted 8 lines
Quoted 3 lines

Comments are bound to be wrong in the future, often near future. It's better to not use the feature, because it's not necessary. Just use [2], and ask your compiler to be better than what ISO C requires.

[static ...] implies that the pointer is not null, this is a standard compliant way to express this (it implies a bit more as nonnull, but compilers do not support more). Still, it gives you a real practical security benefit.

I know you can get usefulness out of it, but the fact that you must express a bound and that it doesn't mean what everyone would expect it to mean makes it a broken feature, unless and until we fix the second bullet by making [static n] mean the same as [[gnu::nonnull]] [n], which is n3801 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3801.txt>.

Quoted 66 lines

void foo(int a[static 3]);
void bar()

{
	foo(nullptr);
}

<source>: In function 'bar':
<source>:5:9: warning: argument 1 null where non-null expected [-Wnonnull]

    5 |         foo(nullptr);
      |         ^~~
<source>:2:6: note: in a call to function 'foo' declared 'nonnull'
    2 | void foo(int a[static 3]);

https://godbolt.org/z/fa4YsWq63

Quoted 16 lines
Quoted 16 lines
Quoted 6 lines

- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

Of the compilers I have tried, only SDCC currently warns about access past the specified length, and it does so regardless of whether the length is specified using 'static' or not:

int foo(int a[static 2])
{
return a[2]; // warning from SDCC
}

Quoted 9 lines
Quoted 6 lines

int bar(int a[2])
{
return a[2]; // warning from SDCC
}

https://godbolt.org/z/zf3nPerdn

GCC has some partial diagnostics for out-of-bounds accesses.

... (example) ...

Quoted 2 lines

I wish it diagnosed as much as SDCC, but not yet. :)

The more relevant example is this:

void foo(int n; int a[n], int n);
void bar(int a[4])

{
	foo(a, 5);
}

<source>: In function 'bar':
<source>:5:9: warning: 'foo' accessing 20 bytes in a region of size 16 [-Wstringop-overflow=]

    5 |         foo(a, 5);
      |         ^~~~~~~~~
<source>:5:9: note: referencing argument 1 of type 'int[]'
<source>:2:6: note: in a call to function 'foo'
    2 | void foo(int n; int a[n], int n);

https://godbolt.org/z/hhGYTej49

This has a very real and obvious security benefit.

Quoted 1 line

...

Quoted 7 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

It was not a mistake, it was a different set of priorities

It was a mistake. They thought it would provide room for optimizations, but nobody checked whether it was actually useful in the real world. This seems to me a clear example of a design looking for users, which is precisely what the C Committee should *not* do.

Quoted 46 lines

We now have more users and people interested in safety in the committee, but powers could easily shift back.

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

and I so I don't dislike it, And it is
standard C.

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

Quoted 31 lines
Quoted 6 lines
Quoted 1 line

People clearly don't understand [static 2] either.

My understanding is that [static 2] was introduced to facilitate cases like the following, where an implementation could theoretically generate code that reads the first four elements together, but would have to load subsequent elements individually:

My understanding is tthe same.

Quoted 22 lines

int len(int s[static 4])
{
int i = 0;
do

  {
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;

} while (1);
return i;
}
https://godbolt.org/z/ra6zh7Ej3

I have never yet managed to persuade a compiler to optimise such code differently from a version without 'static 4', and I struggle to believe it would ever be beneficial to do so. The more obvious case, where an array bound such as [static 4 * n] potentially enables speculative loads when unrolling a loop with an early exit, does not need the compiler to be permitted to generate code that reads beyond the passed-in array bound.

Quoted 2 lines

I think the idea of limiting access to the rest of the array did not occur to them, as this feature was only for optimization.

And that's what I call a mistake; a big one.

Quoted 9 lines
Quoted 4 lines

And indeed, I agree. This seems a case of bad design, wishing that it might eventually be optimizable, while it wasn't and still isn't, and also while ignoring the safety issues caused by it. We should just pretend that the feature doesn't exist.

It would potentially be useful for optimization, but I think there was a chicken-and-egg problem. Nobody used [static] and then people worked on instead optimizations more useful for existing code, so [static] also never become useful for programmers.

That's the right approach.

Quoted 1 line

Much later it become useful to express non-null pointers.

It still hasn't. It will once we get rid of the bound. That is, it will be useful when one can do [static] or [static *]. And even then, the fact that it can't be used on function pointers makes it a bad patch in the language. We need a feature that is consistent.

And we also have the issue that it changes the meaning of the bound. It's just terrible.

Have a lovely day!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#16 lore raw

Am Sonntag, dem 25.10.2026 um 07:59 +0100 schrieb Alejandro Colomar:

Quoted 53 lines

Hi Martin,

Quoted 42 lines

Date: 2026-10-24 20:11:10+0200
From: Martin Uecker <[email protected]>

Am Samstag, dem 24.10.2026 um 18:37 +0200 schrieb Alejandro Colomar:

Quoted 38 lines

Hi Chris,

Quoted 9 lines

Date: 2026-10-24 17:16:19+0100
From: Christopher Bazley <[email protected]>

Quoted 4 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-24 09:45:02-0300
From: "Michael Kerrisk (man7.org)" <[email protected]>

[...]

Quoted 25 lines
Quoted 12 lines
Quoted 2 lines

I was unfamiliar with that syntax, but at least in context it is immediately understandable,

Actually, it is not understandable. [static 2] doesn't mean the same as [2], and this is one historic mistake of the C Committee that I wish had never happened. [static 2] is never what you want (I was tempted to say almost never, but I've never seen a good case for it). Programmers want the meaning of [2].

The main differences between [static 2] and [2] are:

(quoted diff) +0 -1
-  [2] means (in GNU C, and in common understanding, but not

Review

Quoted 10 lines
(quoted diff) +0 -1
   acknowledged by ISO C) that:
 
        -  The caller must supply at least 2 elements.
        -  The callee must not access past those 2 elements.
 
-  [static 2] means (in ISO C, but ignored by GCC):
 
        -  The caller must supply at least 2 elements.
        -  The callee is free to access past those 2 elements!!
           (of course, if there aren't any elements after that, it's UB)

You can, of course, document for the benefit of callers that a function does not read past the static array bound.

Quoted 8 lines
Quoted 3 lines

Comments are bound to be wrong in the future, often near future. It's better to not use the feature, because it's not necessary. Just use [2], and ask your compiler to be better than what ISO C requires.

[static ...] implies that the pointer is not null, this is a standard compliant way to express this (it implies a bit more as nonnull, but compilers do not support more). Still, it gives you a real practical security benefit.

Quoted 5 lines

I know you can get usefulness out of it, but the fact that you must express a bound and that it doesn't mean what everyone would expect it to mean makes it a broken feature, unless and until we fix the second bullet by making [static n] mean the same as [[gnu::nonnull]] [n], which is n3801 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3801.txt>.

I know. Is this on the agenda?

Quoted 73 lines
Quoted 73 lines

void foo(int a[static 3]);
void bar()

{
	foo(nullptr);
}

<source>: In function 'bar':
<source>:5:9: warning: argument 1 null where non-null expected [-Wnonnull]

    5 |         foo(nullptr);
      |         ^~~
<source>:2:6: note: in a call to function 'foo' declared 'nonnull'
    2 | void foo(int a[static 3]);

https://godbolt.org/z/fa4YsWq63

Quoted 16 lines
Quoted 16 lines
Quoted 6 lines

- The caller must supply a non-null pointer.

The null-pointer issue is nonsense, but we can live with it. The allowance to access past the specified length, that's the real issue with this feature, and why it should not be used at all.

Of the compilers I have tried, only SDCC currently warns about access past the specified length, and it does so regardless of whether the length is specified using 'static' or not:

int foo(int a[static 2])
{
return a[2]; // warning from SDCC
}

Quoted 9 lines
Quoted 6 lines

int bar(int a[2])
{
return a[2]; // warning from SDCC
}

https://godbolt.org/z/zf3nPerdn

GCC has some partial diagnostics for out-of-bounds accesses.

... (example) ...

Quoted 2 lines

I wish it diagnosed as much as SDCC, but not yet. :)

The more relevant example is this:

void foo(int n; int a[n], int n);
void bar(int a[4])

{
	foo(a, 5);
}

<source>: In function 'bar':
<source>:5:9: warning: 'foo' accessing 20 bytes in a region of size 16 [-Wstringop-overflow=]

    5 |         foo(a, 5);
      |         ^~~~~~~~~
<source>:5:9: note: referencing argument 1 of type 'int[]'
<source>:2:6: note: in a call to function 'foo'
    2 | void foo(int n; int a[n], int n);

https://godbolt.org/z/hhGYTej49

This has a very real and obvious security benefit.

Quoted 7 lines

...

Quoted 5 lines
Quoted 5 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

Quoted 6 lines
Quoted 1 line

It was not a mistake, it was a different set of priorities

It was a mistake. They thought it would provide room for optimizations, but nobody checked whether it was actually useful in the real world. This seems to me a clear example of a design looking for users, which is precisely what the C Committee should *not* do.

I would assume it came from cray and they had real world use case for this at that time.

Quoted 62 lines
Quoted 47 lines

We now have more users and people interested in safety in the committee, but powers could easily shift back.

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

and I so I don't dislike it, And it is
standard C.

When the standard clashes with reality and common sense, I'm not sure I'd give the standard much credibility.

Quoted 3 lines

I really don't think Linux man-pages is the place to
introducing nonstandard, GNU-specific syntax (that people clearly don't understand).

Quoted 31 lines
Quoted 6 lines
Quoted 1 line

People clearly don't understand [static 2] either.

My understanding is that [static 2] was introduced to facilitate cases like the following, where an implementation could theoretically generate code that reads the first four elements together, but would have to load subsequent elements individually:

My understanding is tthe same.

Quoted 22 lines

int len(int s[static 4])
{
int i = 0;
do

  {
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;
    if (!s[i]) break; else ++i;

} while (1);
return i;
}
https://godbolt.org/z/ra6zh7Ej3

I have never yet managed to persuade a compiler to optimise such code differently from a version without 'static 4', and I struggle to believe it would ever be beneficial to do so. The more obvious case, where an array bound such as [static 4 * n] potentially enables speculative loads when unrolling a loop with an early exit, does not need the compiler to be permitted to generate code that reads beyond the passed-in array bound.

Quoted 2 lines

I think the idea of limiting access to the rest of the array did not occur to them, as this feature was only for optimization.

And that's what I call a mistake; a big one.

Quoted 9 lines
Quoted 4 lines

And indeed, I agree. This seems a case of bad design, wishing that it might eventually be optimizable, while it wasn't and still isn't, and also while ignoring the safety issues caused by it. We should just pretend that the feature doesn't exist.

It would potentially be useful for optimization, but I think there was a chicken-and-egg problem. Nobody used [static] and then people worked on instead optimizations more useful for existing code, so [static] also never become useful for programmers.

Quoted 8 lines

That's the right approach.

Quoted 1 line

Much later it become useful to express non-null pointers.

It still hasn't. It will once we get rid of the bound. That is, it will be useful when one can do [static] or [static *]. And even then, the fact that it can't be used on function pointers makes it a bad patch in the language. We need a feature that is consistent.

Yes, but we do not even have a proposal and [static 1] exists today.

Quoted 3 lines

And we also have the issue that it changes the meaning of the bound. It's just terrible.

The bound in [1] has no meaning in parameters, so [static 1] changes the meaning only in a sense that that it has a stricter meaning that is still only a subset of what we want.

Martin

Quoted 4 lines

Have a lovely day!
Alex

#17 lore raw

Hi Martin,

Quoted 9 lines

Date: 2026-10-25 08:22:23+0100
From: Martin Uecker <[email protected]>

Am Sonntag, dem 25.10.2026 um 07:59 +0100 schrieb Alejandro Colomar:

Quoted 5 lines

Hi Martin,

Quoted 3 lines

Date: 2026-10-24 20:11:10+0200
From: Martin Uecker <[email protected]>

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

[static ...] implies that the pointer is not null, this is a standard compliant way to express this (it implies a bit more as nonnull, but compilers do not support more). Still, it gives you a real practical security benefit.

Quoted 7 lines
Quoted 5 lines

I know you can get usefulness out of it, but the fact that you must express a bound and that it doesn't mean what everyone would expect it to mean makes it a broken feature, unless and until we fix the second bullet by making [static n] mean the same as [[gnu::nonnull]] [n], which is n3801 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3801.txt>.

I know. Is this on the agenda?

No; it has wording issues pointed out by Joseph, and I don't have the time or energy to address them at the moment. See [SC22WG14.36403] in the mailing list.

Feel free to take over the paper if you have a good idea for those. I plan to come back to it, but not this year.

[...]

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

The committee was clearly confused when they added this feature. It was around the time when 'restrict' was added, which is another historical mistake of the committee. In general, every feature they added thinking of optimizations (and that includes these two) has resulted in a mistake.

It was not a mistake, it was a different set of priorities

Quoted 7 lines
Quoted 4 lines

It was a mistake. They thought it would provide room for optimizations, but nobody checked whether it was actually useful in the real world. This seems to me a clear example of a design looking for users, which is precisely what the C Committee should *not* do.

I would assume it came from cray and they had real world use case for this at that time.

What is 'cray'?

[...]

Quoted 17 lines
Quoted 17 lines
Quoted 2 lines

I think the idea of limiting access to the rest of the array did not occur to them, as this feature was only for optimization.

And that's what I call a mistake; a big one.

Quoted 9 lines
Quoted 4 lines

And indeed, I agree. This seems a case of bad design, wishing that it might eventually be optimizable, while it wasn't and still isn't, and also while ignoring the safety issues caused by it. We should just pretend that the feature doesn't exist.

It would potentially be useful for optimization, but I think there was a chicken-and-egg problem. Nobody used [static] and then people worked on instead optimizations more useful for existing code, so [static] also never become useful for programmers.

That's the right approach.

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

Much later it become useful to express non-null pointers.

It still hasn't. It will once we get rid of the bound. That is, it will be useful when one can do [static] or [static *]. And even then, the fact that it can't be used on function pointers makes it a bad patch in the language. We need a feature that is consistent.

Yes, but we do not even have a proposal and [static 1] exists today.

Actually, we do have one: n3803.
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3803.txt>

The wording on this one is good. I think I forgot some trivial duplication of the diff for both the non-abstract and the abstract declarators, but I can fix that when I arrive home, so we could have it in time for the next meeting.

Quoted 5 lines
Quoted 3 lines

And we also have the issue that it changes the meaning of the bound. It's just terrible.

The bound in [1] has no meaning in parameters,

This is not true. ISO C is just toilet paper in this regard. It has a meaning in SDCC and GCC, and every programmer that ever wrote
[1] assigned a meaning to it. Moreover, I'm using [1] myself to express pointers to exactly 1 element.

Quoted 3 lines

so [static 1] changes
the meaning only in a sense that that it has a stricter meaning that is still only a subset of what we want.

This is just not true. The standard is meaningless when it clashes with reality and common sense. As Capt. Barbossa would say, "the [standard] is more what you'd call "guidelines" than actual rules. Welcome aboard the Black Pearl, Miss Turner!"

Cheers,
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#18 lore raw
Quoted 6 lines · Alejandro Colomar
Quoted 4 lines

Date: 2026-10-25 08:22:23+0100
From: Martin Uecker <[email protected]>
I would assume it came from cray and they had real world use case for this at that time.

What is 'cray'?

Seymour Cray, founder of Cray Research, commonly known as "the father of supercomputing".

The Wikipedia articles on him and/or the company will get you up to speed.

Like John Nash, he might have lived longer but for careless drivers.

Regards,
Branden

#19 lore raw
In reply to Alejandro Colomar

Am Sonntag, dem 25.10.2026 um 08:38 +0100 schrieb Alejandro Colomar:

Quoted 3 lines

Hi Martin,

Quoted 1 line

...

Quoted 10 lines
Quoted 9 lines
Quoted 7 lines
Quoted 2 lines

Much later it become useful to express non-null pointers.

It still hasn't. It will once we get rid of the bound. That is, it will be useful when one can do [static] or [static *]. And even then, the fact that it can't be used on function pointers makes it a bad patch in the language. We need a feature that is consistent.

Yes, but we do not even have a proposal and [static 1] exists today.

Quoted 7 lines

Actually, we do have one: n3803.
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3803.txt>

The wording on this one is good. I think I forgot some trivial duplication of the diff for both the non-abstract and the abstract declarators, but I can fix that when I arrive home, so we could have it in time for the next meeting.

Yes, please. But this would also not work for function pointer, so I thought you meant something new.

Quoted 11 lines
Quoted 6 lines
Quoted 4 lines

And we also have the issue that it changes the meaning of the bound. It's just terrible.

The bound in [1] has no meaning in parameters,

This is not true. ISO C is just toilet paper in this regard. It has a meaning in SDCC and GCC, and every programmer that ever wrote
[1] assigned a meaning to it. Moreover, I'm using [1] myself to express pointers to exactly 1 element.

In GCC [static n] and [n] have the same meaning, except that [static] additionally implies that the pointer is not null. I have worked on this, so please assume I know this.

In ISO C it is as I said, [n] has no meaning and [static n] is a strict subset of what we want.

So I am really confused about what you are trying to tell me.

Martin

Quoted 13 lines
Quoted 4 lines

so [static 1] changes
the meaning only in a sense that that it has a stricter meaning that is still only a subset of what we want.

This is just not true. The standard is meaningless when it clashes with reality and common sense. As Capt. Barbossa would say, "the [standard] is more what you'd call "guidelines" than actual rules. Welcome aboard the Black Pearl, Miss Turner!"

Cheers,
Alex

#20 lore raw

Hi Martin,

Quoted 4 lines

Date: 2026-10-25 09:40:52+0100
From: Martin Uecker <[email protected]>

Am Sonntag, dem 25.10.2026 um 08:38 +0100 schrieb Alejandro Colomar:

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 1 line

Yes, but we do not even have a proposal and [static 1] exists today.

Actually, we do have one: n3803.
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3803.txt>

Quoted 7 lines
Quoted 4 lines

The wording on this one is good. I think I forgot some trivial duplication of the diff for both the non-abstract and the abstract declarators, but I can fix that when I arrive home, so we could have it in time for the next meeting.

Yes, please. But this would also not work for function pointer, so I thought you meant something new.

Done. I've asked Dan for a number, and sent the draft to the mailing list.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

And we also have the issue that it changes the meaning of the bound. It's just terrible.

The bound in [1] has no meaning in parameters,

Quoted 8 lines
Quoted 4 lines

This is not true. ISO C is just toilet paper in this regard. It has a meaning in SDCC and GCC, and every programmer that ever wrote
[1] assigned a meaning to it. Moreover, I'm using [1] myself to express pointers to exactly 1 element.

In GCC [static n] and [n] have the same meaning, except that [static] additionally implies that the pointer is not null. I have worked on this, so please assume I know this.

Indeed, I know you know.

Quoted 2 lines

In ISO C it is as I said, [n] has no meaning and [static n] is a strict subset of what we want.

And I insist that ISO C should be ignored. It doesn't matter what ISO C says about it, and your statement that [1] has no meaning in parameters is giving it too much credibility.

Quoted 1 line

So I am really confused about what you are trying to tell me.

I'm trying to say that [1] has a meaning, even outside of GCC. Every programmer that ever wrote [n] meant the obvious thing. The fact that ISO C ignores this shouldn't be an excuse to claim that [static n] doesn't change the meaning, because it is a de-facto change of meaning.

Cheers,
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#21 lore raw

Thanks. I think we are on the same page on this.
And thanks for pushing this forward.

Martin

Am Sonntag, dem 25.10.2026 um 09:57 +0100 schrieb Alejandro Colomar:

Quoted 56 lines

Hi Martin,

Quoted 4 lines

Date: 2026-10-25 09:40:52+0100
From: Martin Uecker <[email protected]>

Am Sonntag, dem 25.10.2026 um 08:38 +0100 schrieb Alejandro Colomar:

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 1 line

Yes, but we do not even have a proposal and [static 1] exists today.

Actually, we do have one: n3803.
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3803.txt>

Quoted 7 lines
Quoted 4 lines

The wording on this one is good. I think I forgot some trivial duplication of the diff for both the non-abstract and the abstract declarators, but I can fix that when I arrive home, so we could have it in time for the next meeting.

Yes, please. But this would also not work for function pointer, so I thought you meant something new.

Done. I've asked Dan for a number, and sent the draft to the mailing list.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

And we also have the issue that it changes the meaning of the bound. It's just terrible.

The bound in [1] has no meaning in parameters,

Quoted 8 lines
Quoted 4 lines

This is not true. ISO C is just toilet paper in this regard. It has a meaning in SDCC and GCC, and every programmer that ever wrote
[1] assigned a meaning to it. Moreover, I'm using [1] myself to express pointers to exactly 1 element.

In GCC [static n] and [n] have the same meaning, except that [static] additionally implies that the pointer is not null. I have worked on this, so please assume I know this.

Indeed, I know you know.

Quoted 2 lines

In ISO C it is as I said, [n] has no meaning and [static n] is a strict subset of what we want.

And I insist that ISO C should be ignored. It doesn't matter what ISO C says about it, and your statement that [1] has no meaning in parameters is giving it too much credibility.

Quoted 1 line

So I am really confused about what you are trying to tell me.

I'm trying to say that [1] has a meaning, even outside of GCC. Every programmer that ever wrote [n] meant the obvious thing. The fact that ISO C ignores this shouldn't be an excuse to claim that [static n] doesn't change the meaning, because it is a de-facto change of meaning.

Cheers,
Alex

#22 lore raw
In reply to Martin Uecker
Quoted 20 lines · Martin Uecker

Am Sonntag, dem 25.10.2026 um 08:38 +0100 schrieb Alejandro Colomar:

Quoted 3 lines

Hi Martin,

Quoted 1 line

...

Quoted 13 lines
Quoted 9 lines
Quoted 7 lines
Quoted 2 lines

Much later it become useful to express non-null pointers.

It still hasn't. It will once we get rid of the bound. That is, it will be useful when one can do [static] or [static *]. And even then, the fact that it can't be used on function pointers makes it a bad patch in the language. We need a feature that is consistent.

Yes, but we do not even have a proposal and [static 1] exists today.

Actually, we do have one: n3803.
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3803.txt>

Quoted 7 lines
Quoted 4 lines

The wording on this one is good. I think I forgot some trivial duplication of the diff for both the non-abstract and the abstract declarators, but I can fix that when I arrive home, so we could have it in time for the next meeting.

Yes, please. But this would also not work for function pointer, so I thought you meant something new.

I don't think that C needs new syntax to express non-null pointers to functions just because null pointer constants can be implicitly converted to pointers to unqualified types, any more than it needs new syntax to express pointers to mutable objects just because "" engenders an array of type char []. That would be tackling the problem from the wrong end.

I already have a solution that works for me: my fork of Clang (
https://github.com/chrisbazley/llvm-project ). For example, this function requires a non-null callback function pointer:

bool ObjEditSelection_for_each_changed(ObjEditSelection const *a,
ObjEditSelection const *b,
_Optional MapArea const *map_area,
void (*callback)(MapPoint, void *), void *cb_arg);

https://github.com/chrisbazley/SFEditor/blob/8971ff7bc2ad047be637b4b09f185ebd4ac8bc5b/ObjEditSel.h#L105-L108

To demonstrate, I modified one of its callers to pass a null pointer constant instead of a function pointer:

    ObjEditSelection_for_each_changed(&mode_data->occluded.obj,

&mode_data->tmp.obj, // previously occluded
- NULL, occluded_changed, editor);
+ NULL, NULL, editor);

https://github.com/chrisbazley/SFEditor/commit/8971ff7bc2ad047be637b4b09f185ebd4ac8bc5b

In GitHub CI, I enable -Werror=null-to-nonoptional for all of my projects. As intended, the above modification results in a build failure:

/home/runner/work/SFEditor/SFEditor/ObjectsMode.c:669:14: error: null pointer constant converted to a pointer whose referenced type is not '_Optional'-qualified [clang-diagnostic-null-to-nonoptional]
669 | NULL, NULL, editor);
| ^
/home/runner/work/_temp/optional-clang/lib/clang/23/include/__stddef_null.h:26:15:
note: expanded from macro 'NULL'
26 | #define NULL ((void*)0)
| ^
1 error generated.

https://github.com/chrisbazley/SFEditor/actions/runs/40923220788/job/123621744519

I don't want anything like this (fictitious) syntax:

bool ObjEditSelection_for_each_changed(ObjEditSelection const *a,
ObjEditSelection const *b,
_Optional MapArea const *map_area,
void callback[static](MapPoint, void *), void *cb_arg);

What C actually needs is a standard extension to the direct declarator syntax to allow pointers to functions of qualified type. Even disregarding _Optional, SDCC's __reentrant and __banked keywords would benefit from such standard syntax. SDCC currently has inconsistencies in this area, for example https://sourceforge.net/p/sdcc/bugs/3989/
and https://sourceforge.net/p/sdcc/bugs/4027/

Although the keywords __reentrant and __banked appear syntactically in the position of a C23 attribute, I consider them type qualifiers rather than attributes because ignoring them can change the calling convention and make calls incorrect. They also participate in type checking:

int reentrant(int *) __reentrant;
int banked(int *) __banked;
int plain(int *);

int main(void)
{
int (*r)(int *) __reentrant = reentrant;
r = plain; // how is this safe?
r = banked; // error

int (*b)(int *) __banked = banked;
b = plain; // error
b = reentrant; // error

int (*p)(int *) = plain;
p = reentrant;
p = banked; // error

return 0;
}
https://godbolt.org/z/hPsxWe6Ga

I increasingly think this is also the right position for the qualifier in a declaration of a pointer to
an optional function, e.g.

int (*o)(int *) _Optional = fn;

as a possible alternative to

_Optional typeof(int (int *)) *o = fn;

Currently, type-qualifier-list appears after attribute-specifier-sequence in both alternatives for 'pointer' but not elsewhere it would be needed, such as after function-declarator in one of the alternatives for direct-declarator, nor after function-abstract-declarator in one of the alternatives for direct-abstract-declarator (which is where it would need to be permitted to allow qualified function type names to appear in casts).

Best regards,
Christopher

#23 lore raw

Hi Chris,

Quoted 3 lines

Date: 2026-10-29 21:24:14+0000
From: Christopher Bazley <[email protected]>

[...]

Quoted 6 lines

I don't want anything like this (fictitious) syntax:

bool ObjEditSelection_for_each_changed(ObjEditSelection const *a,
ObjEditSelection const *b,
_Optional MapArea const *map_area,
void callback[static](MapPoint, void *), void *cb_arg);

Indeed, me neither. Just to clarify, this will still be disallowed by my paper.

Quoted 6 lines

What C actually needs is a standard extension to the direct declarator syntax to allow pointers to functions of qualified type. Even disregarding _Optional, SDCC's __reentrant and __banked keywords would benefit from such standard syntax. SDCC currently has inconsistencies in this area, for example https://sourceforge.net/p/sdcc/bugs/3989/
and https://sourceforge.net/p/sdcc/bugs/4027/

Also agree. In fact, I believe [[reproducible]] should actually be a _Reproducible function qualifier (actually, an anti-qualifier). I plan to work on a paper for that, but I need several things to happen first:

- Non-access qualifiers to be standardized. Several function qualifiers, to avoid annotating most functions, should be anti-qualifiers.

I believe the paper about address-space qualifiers will help in this regard.

- Type variance. This is essential for backwards compatiblity.

Quoted 32 lines

Although the keywords __reentrant and __banked appear syntactically in the position of a C23 attribute, I consider them type qualifiers rather than attributes because ignoring them can change the calling convention and make calls incorrect. They also participate in type checking:

int reentrant(int *) __reentrant;
int banked(int *) __banked;
int plain(int *);

int main(void)
{
int (*r)(int *) __reentrant = reentrant;
r = plain; // how is this safe?
r = banked; // error

int (*b)(int *) __banked = banked;
b = plain; // error
b = reentrant; // error

int (*p)(int *) = plain;
p = reentrant;
p = banked; // error

return 0;
}
https://godbolt.org/z/hPsxWe6Ga

I increasingly think this is also the right position for the qualifier in a declaration of a pointer to
an optional function, e.g.

Quoted 5 lines

int (*o)(int *) _Optional = fn;

as a possible alternative to

_Optional typeof(int (int *)) *o = fn;

Also agree. My idea was to have this:

	size_t strlen(const char *s) _Reproducible;

and collapse both attributes into a single qualifier. We don't need a tri-state, IMO.

Have a lovely night!
Alex

Quoted 10 lines

Currently, type-qualifier-list appears after attribute-specifier-sequence in both alternatives for 'pointer' but not elsewhere it would be needed, such as after function-declarator in one of the alternatives for direct-declarator, nor after function-abstract-declarator in one of the alternatives for direct-abstract-declarator (which is where it would need to be permitted to allow qualified function type names to appear in casts).

Best regards,
Christopher

Signature · 1 line
<https://www.alejandro-colomar.es>
#24 lore raw
In reply to Alejandro Colomar
Quoted 6 lines

Date: 2026-10-22 21:54:09+0200
From: Alejandro Colomar <[email protected]>

Quoted 3 lines

Date: 2026-10-22 18:29:31+0100
From: Christopher Bazley <[email protected]>

[...]

Quoted 10 lines
Quoted 5 lines

but the fact that there are no useful ISO C
semantics for parameters of pointer type declared using array syntax weakens my concern. (If the parameters actually had variably-modified types then the change would be far worse, from my point of view, because the parameter types would no longer be expressed correctly.)

We don't care about what ISO C says. GNU C clearly assigns semantics to array parameters, and that means we have to be careful about them here. If this can result in sanitizers breaking code, it is something to be careful about. Moreover, I hope ISO C eventually catches up.

Moreover, there's GCC discussing at the moment a patch of mine to have _Countof(parameter) work, which would be an even larger risk: it would produce different values in code, and not just sanitizers aborting some program.

Have a lovely day!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#25 lore raw
In reply to Alejandro Colomar
Quoted 63 lines · Alejandro Colomar

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Concerns that have been raised against using the GNU C syntax in the manual pages:

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Quoted 7 lines

Do we think programmers are unable to ever learn this new syntax? Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

The man pages are frequently used when seeing a call to an unfamiliar function, to understand what the function expects and what it is doing with those arguments. The arguments in the call are matched to the synopsis by order, not by name, so for example a person questioning the first argument would naturally be looking at the first thing after the "(" to find out what is expected there.

A person that is reading the description, where it refers to the arguments by name, needs to convert that name to the argument number in order to match it to the arguments in the actual call. The natural way to do that is to count the argument names in the synopsis (at least those not preceded by an unterminated bracket or comment) and stop when that name is found. For example if that is the first argument name after the "(" in the synopsis then it would be natural to conclude that it matches the first argument to the call, and not continue scanning to see if there is another occurrence of the same argument name.

So I agree with Michael that this change is a good one to make the man pages less confusing. Thanks for creating the change.

- Mark

#26 lore raw

Hi Mark,

Quoted 3 lines

Date: 2026-10-22 15:03:37-0700
From: Mark Harris <[email protected]>

[...]

Quoted 8 lines

The man pages are frequently used when seeing a call to an unfamiliar function, to understand what the function expects and what it is doing with those arguments. The arguments in the call are matched to the synopsis by order, not by name, so for example a person questioning the first argument would naturally be looking at the first thing after the "(" to find out what is expected there.

Quoted 10 lines

A person that is reading the description, where it refers to the arguments by name, needs to convert that name to the argument number in order to match it to the arguments in the actual call. The natural way to do that is to count the argument names in the synopsis (at least those not preceded by an unterminated bracket or comment) and stop when that name is found. For example if that is the first argument name after the "(" in the synopsis then it would be natural to conclude that it matches the first argument to the call, and not continue scanning to see if there is another occurrence of the same argument name.

That's a valid concern, but most often the first argument will be of a different and incompatible type (they tend to be pointers, which is the reason we need the forward declaration in the first place), and thus such a mistake in the understanding of the order of arguments will result in a compiler error, which is a relatively mild problem.

You didn't take into account my other concerns, though. I'd like to hear your opinion about the concerns that letting users think that is valid C might lead to them writing code that has UB (or logic errors).

Quoted 2 lines

So I agree with Michael that this change is a good one to make the man pages less confusing. Thanks for creating the change.

Thanks for your feedback!

Quoted 2 lines

- Mark

Have a lovely day!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#27 lore raw
Quoted 26 lines · Alejandro Colomar

Hi Mark,

Quoted 3 lines

Date: 2026-10-22 15:03:37-0700
From: Mark Harris <[email protected]>

[...]

Quoted 8 lines

The man pages are frequently used when seeing a call to an unfamiliar function, to understand what the function expects and what it is doing with those arguments. The arguments in the call are matched to the synopsis by order, not by name, so for example a person questioning the first argument would naturally be looking at the first thing after the "(" to find out what is expected there.

Quoted 10 lines

A person that is reading the description, where it refers to the arguments by name, needs to convert that name to the argument number in order to match it to the arguments in the actual call. The natural way to do that is to count the argument names in the synopsis (at least those not preceded by an unterminated bracket or comment) and stop when that name is found. For example if that is the first argument name after the "(" in the synopsis then it would be natural to conclude that it matches the first argument to the call, and not continue scanning to see if there is another occurrence of the same argument name.

Quoted 5 lines

That's a valid concern, but most often the first argument will be of a different and incompatible type (they tend to be pointers, which is the reason we need the forward declaration in the first place), and thus such a mistake in the understanding of the order of arguments will result in a compiler error, which is a relatively mild problem.

I am not talking about someone writing new code that they will then try to compile at some point, I am talking about someone trying to understand or debug existing code.

For example, they may be looking for all code that writes to some particular data or performs some particular operation on it. They see that a pointer to the data is passed to various functions, and then for any unfamiliar function they may quickly check the man page to determine whether they can rule out this call as unrelated. Because C function calls use positional syntax but the man pages describe the function of the arguments using argument names, they will have to count the arguments in the synopsis, which will give them an argument name, and then they can search for the argument name in the description to find out what the function does with that argument. If the counting is complicated they may end up with the wrong argument name and determine that the function does not perform any action of interest on this argument, resulting in failure to complete the task. Even long-time C users may be foiled by the forward declaration, but it is possible that the person does not even know C; perhaps they are calling into this C library from another language binding and are trying to debug what is happening. The man pages should do what it can to help them, not throw monkey wrenches in their path.

Quoted 4 lines

You didn't take into account my other concerns, though. I'd like to hear your opinion about the concerns that letting users think that is valid C might lead to them writing code that has UB (or logic errors).

An error in thinking that this is valid C syntax doesn't impact anyone trying to understand existing code, so I don't see the concerns as comparable.

If someone adds this in their own C code, and it has not been adopted as valid ISO C or as a compiler extension, then it is extremely likely to result in a compiler error. If a likely compiler error was good enough for you when someone writes the arguments in the wrong order in their code due to confusion over the syntax, then I don't see why it wouldn't be satisfactory in this case where a compiler error is even more likely. In both cases there is a chance that it will be accepted and do the wrong thing.

- Mark

Quoted 15 lines
Quoted 3 lines

So I agree with Michael that this change is a good one to make the man pages less confusing. Thanks for creating the change.

Thanks for your feedback!

Quoted 2 lines

- Mark

Have a lovely day!
Alex

--
<https://www.alejandro-colomar.es>

#28 lore raw

Hi Mark,

Quoted 3 lines

Date: 2026-10-23 12:32:37-0700
From: Mark Harris <[email protected]>

[...]

Quoted 5 lines

I am not talking about someone writing new code that they will then try to compile at some point, I am talking about someone trying to understand or debug existing code.

[...]

Quoted 2 lines

trying to debug what is happening. The man pages should do what it can to help them, not throw monkey wrenches in their path.

I see your concern.

Quoted 7 lines
Quoted 3 lines

You didn't take into account my other concerns, though. I'd like to hear your opinion about the concerns that letting users think that is valid C might lead to them writing code that has UB (or logic errors).

An error in thinking that this is valid C syntax doesn't impact anyone trying to understand existing code, so I don't see the concerns as comparable.

It is actually valid syntax already. It doesn't have the meaning that we would be using for it, but it's valid syntax.

	int n = 42;

	int f(char a[n], int n);

	f(a, 7);

The array parameter is declared to have 42 elements, regardless of the 7 used at call site.

Quoted 2 lines

If someone adds this in their own C code, and it has not been adopted as valid ISO C

The code above is valid C89, and it doesn't mean what one would intuitively think it means. That's my concern. This is a source of bugs.

Future extensions to the language will make that even more dangerous, by making it more likely to compile:

	alx@debian:~/tmp$ cat param.c 
	int
	main(void)
	{
		int n = 42;
		int f(char a[n], int n)
		{
			return _Countof(a);
		}

		char s[] = "foo";

		return f(s, _Countof(s));
	}
	alx@debian:~/tmp$ /opt/local/gnu/gcc/cap/bin/gcc -Wall -Wextra param.c 
	param.c: In function ‘f’:
	param.c:6:30: warning: unused parameter ‘n’ [-Wunused-parameter]
	    6 |         int f(char a[n], int n)
	      |                          ~~~~^
	alx@debian:~/tmp$ ./a.out; echo $?
	42
Quoted 7 lines

or as a compiler extension, then it is extremely likely
to result in a compiler error. If a likely compiler error was good enough for you when someone writes the arguments in the wrong order in their code due to confusion over the syntax, then I don't see why it wouldn't be satisfactory in this case where a compiler error is even more likely. In both cases there is a chance that it will be accepted and do the wrong thing.

This analysis is wrong. See above.

Have a lovely night!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#29 lore raw
In reply to Mark Harris

I personally do not quite see much risk for mistakes either way.

I think the more imporant question is whether
man pages will use its role to help make the C ecosystem safer by educating people about forward declarations as a tool that directly improves safety of C code, or whether it wants to remove any friction for users that simply want to get a job done. Both a valid positions.

In light of the fact that safety is a critical problem for old-style C code, I personally would be more happy if it prioritizes safety over use convenience, but this is not my decision.

Martin

Am Donnerstag, dem 22.10.2026 um 15:03 -0700 schrieb Mark Harris:

Quoted 94 lines
Quoted 63 lines · Alejandro Colomar

From: Alejandro Colomar <[email protected]>

From: Alejandro Colomar <[email protected]>

I'm not fully convinced of this patch. But I'm open to being convinced. I've written the patch, as Michael asked me to do, but am willing to hear everyone's opinion before applying the patch.

As Michael reminded me recently, and as I've defended in other cases, it is true that the SYNOPSIS of the manual pages does not need to be strictly valid C. It only needs to be useful enough for readers to understand the interface. Thus, if using lengths without declaring them would improve the usefulness of the documentation, even if becoming invalid C, it would be okay.

However, I still have doubts, because this change comes with risks:

Programmers might think it is valid C to refer to parameters that have not been declared yet, and use that in their code, and if they are unlucky, it might compile for them, which might result in UB at some point.

One valid point is that this is rather unlikely to happen, and most of the time it will just result in a compiler error, because no global variables with the name of the parameter exist.

However, with local (nested) functions, this might be easier to happen, as variables declared in the outer function are more likely to share a name with a length parameter. Given that the C Committee is working hard to standardize local functions, it might be a very bad idea to have this combination of events going on.

Concerns that have been raised against using the GNU C syntax in the manual pages:

Very very few programmers know this syntax, and thus they are confused when reading it for the first time.

This is obviously true. However, this is true of any innovative syntax. We should consider whether the confusion is dangerous or not, and whether programmers can learn it easily or not.

I believe once you've been exposed to this syntax, after some research, or asking the right people, one would easily learn to read it, and then we'd have overcome the issue. I don't think the syntax is inherently difficult to understand once you've seen the documentation and seen it a few times.

Also, this syntax is not very dangerous: a programmer thinking that a forward declaration is actually a normal parameter will pass a wrong number of arguments to the function, and thus will have compiler errors. This syntax is quite robust, as it requires several bugs to happen in the same code to pass compilation silently. If one replaces ';' with ',', the parameter will be declared twice, which is an error. If one renames a forward declaration, it will not match a parameter, and will result in a compiler error. Only if both mistakes happen at once, the compilation would pass, but then the call sites would also have to match the bogus declaration. And also, the function definition would have unused parameters, which would trigger diagnostics. So, concerning safety, this feature is not very dangerous, even if used by someone who is very confused.

Quoted 7 lines

Do we think programmers are unable to ever learn this new syntax? Are we ready to accept the risks of using the dangerous invalid syntax, but which is more intuitive to programmers?

---

The man pages are frequently used when seeing a call to an unfamiliar function, to understand what the function expects and what it is doing with those arguments. The arguments in the call are matched to the synopsis by order, not by name, so for example a person questioning the first argument would naturally be looking at the first thing after the "(" to find out what is expected there.

A person that is reading the description, where it refers to the arguments by name, needs to convert that name to the argument number in order to match it to the arguments in the actual call. The natural way to do that is to count the argument names in the synopsis (at least those not preceded by an unterminated bracket or comment) and stop when that name is found. For example if that is the first argument name after the "(" in the synopsis then it would be natural to conclude that it matches the first argument to the call, and not continue scanning to see if there is another occurrence of the same argument name.

So I agree with Michael that this change is a good one to make the man pages less confusing. Thanks for creating the change.

- Mark