Bug 108694 - need a new warning option for preparing migration to ISO C23
Summary: need a new warning option for preparing migration to ISO C23
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 12.2.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: new-warning, new_warning
  Show dependency treegraph
 
Reported: 2023-02-07 11:57 UTC by Bruno Haible
Modified: 2024-02-21 04:33 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 12.2.0
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Bruno Haible 2023-02-07 11:57:18 UTC
The use of () to denote an unknown or varargs parameter list in function declarations and function types, a language feature from K&R C, is finally disallowed in ISO C 23. Instead, () as a parameter list denotes a list of zero arguments, the same as (void), in ISO C 23.

The set of warnings that were added to GCC over the years, regarding function prototypes, were designed at a time when the migration target was still unknown. Now that the migration target, ISO C 23, is known, some projects want to have the following programming style, for code that compiles OK both in C99 and C23:
- (1) In function definitions, use () to denote argument lists with zero arguments.
- (2) In function declarations and function types, use (void) to denote argument lists with zero arguments.
See https://lists.gnu.org/archive/html/bug-gnulib/2023-02/msg00055.html and https://lists.gnu.org/archive/html/bug-gnulib/2023-02/msg00062.html for details of the rationale.

As a programmer, I would like to have an easy way to get warnings when this programming style is not followed.

There are two ways such a warning could be added to GCC:

(A) A warning that applies when compiling for a language standard older than C23 (e.g. -std=gnu99).

In which situations would this warning be emitted?
=========================================================================
/* Function definitions: */
void func1 () {} /* No warning */
void func2 (void) {} /* No warning */
/* Function declarations: */
void func3 (); /* warning: an empty parameter list in function declarators will have a different meaning in ISO C23 */
void func4 (void); /* No warning */
=========================================================================

Looking through the existing warning options of GCC and clang:

Warning options from https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Option                      Not adequate because ...
-----------------------     ------------------------
-Wall                       Does not warn for func3.
-Wstrict-prototypes         Warns for func1 as well.
-Wold-style-declaration     Does not warn for func3.
-Wold-style-definition      Does not warn for func3. Warns for func1.
-Wmissing-prototypes        Does not warn for func3. Warns for func1 and func2.
-Wmissing-declarations      Does not warn for func3. Warns for func1 and func2.
-Wpedantic                  Does not warn for func3.

Warning options from https://clang.llvm.org/docs/DiagnosticsReference.html
Option                      Not adequate because ...
-----------------------     ------------------------
-Wall                       Does not warn for func3.
-Wdeprecated-declarations   Does not warn for func3.
-Wdeprecated-non-prototype  Does not warn for func3.
-Wmissing-prototypes        Does not warn for func3. Warns for func1 and func2.
-Wpedantic                  Warns for func1 as well.

So, none of these existing warning options fits the need. A new warning option is needed.

(B) A warning that applies when compiling for C23 (e.g. -std=gnu23).

The situations would be the same as above. Only the diagnostic's message would refer to *older* standard versions, such as:
=========================================================================
/* Function definitions: */
void func1 () {} /* No warning */
void func2 (void) {} /* No warning */
/* Function declarations: */
void func3 (); /* warning: an empty parameter list in function declarators denotes a varargs parameter list in ISO C17 and older; use (void) to disambiguate */
void func4 (void); /* No warning */
=========================================================================
Comment 1 Florian Weimer 2023-02-07 12:35:07 UTC
Clang now has -Wdeprecated-non-prototype apparently: https://discourse.llvm.org/t/unresolved-issues-from-the-llvm-15-x-release/66071/36

This is probably not very useful as a warning:

> void func3 (); /* warning: an empty parameter list in function declarators will have a different meaning in ISO C23 */

“()” is going to be fine when matched with an empty parameter list in a definition, or an empty argument list in a call. I don't think it's necessary to warn in those cases. It distracts from the real issue.
Comment 2 Bruno Haible 2023-02-07 20:36:42 UTC
(In reply to Florian Weimer from comment #1)
> “()” is going to be fine when matched with an empty parameter list in a
> definition, or an empty argument list in a call. I don't think it's
> necessary to warn in those cases. It distracts from the real issue.

OK. So let's see what we get with this approach where 'void func3 ();' doesn't warn and only the wrong uses of func3 produce diagnostics. Here's a test case, annotated with 'error' in those places where 'clang15 -std=c2x' produces an error:

=============================================================================
/* Function definitions: */
void func1 () {}
void func2 (void) {}
/* Function declarations: */
void func3 ();
void func4 (void);

void code ()
{
  void (*fp) (void);
  void (*fp1) (int);

  fp = func1;
  fp = func2;
  fp = func3;
  fp = func4;

  fp1 = func3;

  func1 (1); /* error */
  func2 (2); /* error */
  func3 (3); /* error */
  func4 (4); /* error */

  (void) fp;
  (void) fp1;
}

void func3 (int x, int y) {} /* error */
=============================================================================

When compiling this in -std=c17 or older mode, we would like to get a warning
- in those places where we get an error in C23 mode, and
- in those places where there are unsafe conversions or parameter passing going on in C23 or in C17 or older.
In detail, the desired behaviour in -std=c17 or older mode is:

=============================================================================
/* Function definitions: */
void func1 () {} /* No warning */
void func2 (void) {} /* No warning */
/* Function declarations: */
void func3 (); /* No warning */
void func4 (void); /* No warning */

void code ()
{
  void (*fp) (void);
  void (*fp1) (int);

  fp = func1; /* No warning */
  fp = func2; /* No warning */
  fp = func3; /* No warning */
  fp = func4; /* No warning */

  fp1 = func3; /* warning */

  func1 (1); /* warning (if -std=c17) or error (if -std=c23) */
  func2 (2); /* error */
  func3 (3); /* warning (if -std=c17) or error (if -std=c23) */
  func4 (4); /* error */

  (void) fp;
  (void) fp1;
}

void func3 (int x, int y) {} /* warning (if -std=c17) or error (if -std=c23) */
=============================================================================

In the line 'fp1 = func3;' a warning should be shown because it's an unsafe function pointer conversion in C23. (Even though in C17 it is not dangerous code and even though in C23 it's not an error!) 'clang15 -std=c2x -Wincompatible-function-pointer-types' does show a "warning: incompatible function pointer types" there.

> Clang now has -Wdeprecated-non-prototype apparently

But '-Wdeprecated-non-prototype' does not exactly have the behaviour you want: while it warns for 'func1 (1);' and 'func3 (3);' (good!), it warns also for 'void func3 ();', that is, where you don't want to see a warning.

So, no existing GCC or clang warning option has the desired behaviour. What we need (and even independently of programming style) is
* a part of -Wdeprecated-non-prototype: do warn in function call positions, don't warn in function declaration/type positions,
* combined with a kind of -Wfuture-incompatible-function-pointer-types, that considers the interpretation according to C23 instead of the interpretation according to the currently chosen standard.
Comment 3 Aaron Ballman 2023-02-07 20:55:01 UTC
(In reply to Bruno Haible from comment #2)
> But '-Wdeprecated-non-prototype' does not exactly have the behaviour you
> want: while it warns for 'func1 (1);' and 'func3 (3);' (good!), it warns
> also for 'void func3 ();', that is, where you don't want to see a warning.

FWIW, the reason you get a warning on the declaration of `func3` in Clang is because of the definition of `func3` that is invalid in C2x but was valid in prior standards modes. If you do not provide a problematic conflicting definition, the diagnostic is silenced: https://godbolt.org/z/aMPrvWz1j
Comment 4 Bruno Haible 2023-02-07 22:09:08 UTC
(In reply to Aaron Ballman from comment #3)
OK. So, except for this unlucky (*) choice of attribution in case of a conflict between function declaration and function definition, the '-Wdeprecated-non-prototype' warning is actually usable.

What we need is thus:
* -Wdeprecated-non-prototype,
* combined with a kind of -Wfuture-incompatible-function-pointer-types, that considers the interpretation according to C23 instead of the interpretation according to the currently chosen standard.

(*) Reported as a clang bug at https://github.com/llvm/llvm-project/issues/60592 .