[RFC] Enabling -Wstrict-prototypes by default in C

| AaronBallman
March 1 |

  • | - |

Thanks for the explanation!

The status quo in Clang is that we diagnose deprecated constructs in the language modes they’re obsolete in, and your suggestion elides some of those diagnostics in an effort to be less chatty. That goal is nice and something I’m usually in favor of, but is actually opposed to my goal in this case. I want people to know all the places where they’re using a deprecated feature. Any suggestion which boils down to “don’t warn on this even though it’s deprecated” misses that goal of mine.

Default-on warnings should diagnose only code which is very likely to be invalid/broken and needs to be changed. My contention is that code using a no-prototype function as if it was a no-arg prototyped function doesn’t meet that level of wrongness, and therefore we shouldn’t warn on it by default. Yes, such code would be improved by switching to void func(void);, but “can be improved” is not enough to be worth a default-on warning – especially since C2x is coming soon, and improves the code automatically.

Furthermore, saying that such code is “deprecated” feels kind of wrong, now that C2x has fixed it. Indeed, the current semantics are deprecated, but the new semantics for the same syntax are not. C2x has effectively “undeprecated” the syntax void func(); – so long as it’s being used as a no-arg function. There would, perhaps ironically, be a stronger case for emitting a warning on such declarations if C2x hadn’t made this change!

I think it’s important to warn on all deprecated uses because I don’t think we can rely on usage catching all of the issues. e.g.,

void func(); // In a header

void func() { } // In a source file, demonstrates the user intended to use void func(void);

You can still call func through inline assembly and pass it arguments without realizing it.

But, it’s always the case that you can do wrong things in inline asm. Even if the function does have a prototype. This seems unrelated and unfixable.

You can also use func() properly within your library test suite, but then users of your library accidentally misuse the interface in their code.

And we will then warn on those misuses in users’ code. That seems fine?

Also, it’s not clear how to handle cases like using the K&R type in a _Generic selection expression (so there’s no declaration involved, just the type name).

I think this is likely to be so rare that it’s not worth worrying about.

Rather than try to chase down answers to all the myriad of ways you can run into these signatures, I think it’s better to just say “spelling the deprecated type gets you a deprecation warning”. My thinking has been that -Wstrict-prototypes covers exactly the cases I think we need to cover, which is why I was thinking of rewording the diagnostic to be specific to the deprecation issue and then enabling that by default.

We don’t need to diagnose 100% of bad code for a warning diagnostic to be useful/good.

That’s why I don’t think changing the heuristics here gains us much. Additional diagnostics on top of the deprecation warning may be worthwhile, but I sort of think -fstrict-prototypes might (maybe) obviate a need for that work. From your three warning suggestions, in that mode: #1 becomes an error (invalid declaration), #2 becomes an error (invalid function call expression), and #3 becomes an error (invalid redeclaration). And there would be no diagnostics with the fourth case you list. So it does cover your use cases, but with errors instead of warnings (which could be a downside).

I don’t think a flag to change the semantics in older language modes is a solution. We cannot turn it on by default in older standards modes, and having projects go around enabling an extra compiler flag which creates a nonstandard dialect is not great either. But, we can emit warnings by default.