This is an archive of the discontinued LLVM Phabricator instance.

Correctly diagnose prototype redeclaration errors in C
ClosedPublic

Authored by aaron.ballman on Apr 12 2022, 1:32 PM.

Details

Reviewers
jyknight
eli.friedman
erichkeane
Group Reviewers
Restricted Project
Summary

We did not implement C99 6.7.5.3p15 fully in that we missed the rule for compatible function types where a prior declaration has a prototype and a subsequent definition (not just declaration) has an empty identifier list or an identifier list with a mismatch in parameter arity. This addresses that situation by issuing an error on code like:

void f(int);
void f() {} // type conflicts with previous declaration

(Note: we already diagnose the other type conflict situations appropriately, this was the only situation we hadn't covered that I could find.)

Diff Detail

Event Timeline

aaron.ballman created this revision.Apr 12 2022, 1:32 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 12 2022, 1:32 PM
aaron.ballman requested review of this revision.Apr 12 2022, 1:32 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 12 2022, 1:32 PM
erichkeane accepted this revision.Apr 12 2022, 1:35 PM
erichkeane added a subscriber: erichkeane.

This LGTM, please give a day or so for others to take a look.

clang/test/Sema/predefined-function.c
27

Oh my!

This revision is now accepted and ready to land.Apr 12 2022, 1:35 PM
jyknight accepted this revision.Apr 12 2022, 2:11 PM
jyknight added inline comments.
clang/test/Sema/warn-deprecated-non-prototype.c
64–70

Reword to indicate that the second diagnostic is now an error.

aaron.ballman closed this revision.Apr 13 2022, 5:22 AM
aaron.ballman marked an inline comment as done.

Thank you for the reviews, I've landed in 385e7df33046d7292612ee1e3ac00a59d8bc0441.

clang/test/Sema/warn-deprecated-non-prototype.c
64–70

Good catch!

It looks like this caused the LNT build bot to fail (at least on s390x), because one of the test cases now triggers this error:
https://lab.llvm.org/buildbot/#/builders/45/builds/6787

/usr/bin/make -f SingleSource/Regression/C/gcc-c-torture/execute/ieee/CMakeFiles/GCC-C-execute-ieee-compare-fp-4.dir/build.make SingleSource/Regression/C/gcc-c-torture/execute/ieee/CMakeFiles/GCC-C-execute-ieee-compare-fp-4.dir/build
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:6:1: error: conflicting types for 'rintf'
rintf ()
^
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:6:1: note: previous implicit declaration is here
make[2]: Entering directory '/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/sandbox/build'
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:29:14: error: too few arguments to function call, expected 1, have 0
  if (rintf () != -2.0)
      ~~~~~  ^
2 errors generated.

I guess the error is valid, but should either be disabled for this LNT test, or else the test fixed.

aaron.ballman marked an inline comment as done.Apr 14 2022, 4:13 AM

It looks like this caused the LNT build bot to fail (at least on s390x), because one of the test cases now triggers this error:
https://lab.llvm.org/buildbot/#/builders/45/builds/6787

/usr/bin/make -f SingleSource/Regression/C/gcc-c-torture/execute/ieee/CMakeFiles/GCC-C-execute-ieee-compare-fp-4.dir/build.make SingleSource/Regression/C/gcc-c-torture/execute/ieee/CMakeFiles/GCC-C-execute-ieee-compare-fp-4.dir/build
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:6:1: error: conflicting types for 'rintf'
rintf ()
^
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:6:1: note: previous implicit declaration is here
make[2]: Entering directory '/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/sandbox/build'
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/test/test-suite/SingleSource/Regression/C/gcc-c-torture/execute/ieee/20030331-1.c:29:14: error: too few arguments to function call, expected 1, have 0
  if (rintf () != -2.0)
      ~~~~~  ^
2 errors generated.

I guess the error is valid, but should either be disabled for this LNT test, or else the test fixed.

Thank you for letting me know -- I've speculatively fixed the issue in 726901d06aab2f92d684d28507711308368c29d6

Thank you for letting me know -- I've speculatively fixed the issue in 726901d06aab2f92d684d28507711308368c29d6

Can you also look into improving the error message? Just saying "conflicting types for 'rintf'", with a note saying the previous declaration is at the exact same location, is going to confuse anyone who writes float rintf(){}.

Thank you for letting me know -- I've speculatively fixed the issue in 726901d06aab2f92d684d28507711308368c29d6

Can you also look into improving the error message? Just saying "conflicting types for 'rintf'", with a note saying the previous declaration is at the exact same location, is going to confuse anyone who writes float rintf(){}.

FWIW, I agree with you that the diagnostic message is less than helpful. But it's consistent with the message we used in all of the other declaration situations (https://godbolt.org/z/e6hcc4Y3Y) which is why I didn't add a better diagnostic message.

The reason you get the weird behavior with the note pointing to the same line as the declaration is because rintf() is a predefined builtin: https://godbolt.org/z/j3W759M7a (note the same lovely diagnostic note behavior).

So yes, it'd be nice to get better diagnostic behavior in this area, but it's orthogonal to the changes here.

The reason you get the weird behavior with the note pointing to the same line as the declaration is because rintf() is a predefined builtin: https://godbolt.org/z/j3W759M7a (note the same lovely diagnostic note behavior).

It points at the same location, but at least it says "'rintf' is a builtin" (diag::note_previous_builtin_declaration). The new codepath specifically skips the code which emits that note.

The reason you get the weird behavior with the note pointing to the same line as the declaration is because rintf() is a predefined builtin: https://godbolt.org/z/j3W759M7a (note the same lovely diagnostic note behavior).

It points at the same location, but at least it says "'rintf' is a builtin" (diag::note_previous_builtin_declaration). The new codepath specifically skips the code which emits that note.

Oh, good catch! It looks like getNoteDiagForInvalidRedeclaration() doesn't consider using that particular note and it probably should, I'll look into that. Thanks!

Thank you for letting me know -- I've speculatively fixed the issue in 726901d06aab2f92d684d28507711308368c29d6

Yes, the build bot is now green again. Thanks!

Thank you for letting me know -- I've speculatively fixed the issue in 726901d06aab2f92d684d28507711308368c29d6

Yes, the build bot is now green again. Thanks!

Excellent, thank you again for bringing the issue to my attention.

The reason you get the weird behavior with the note pointing to the same line as the declaration is because rintf() is a predefined builtin: https://godbolt.org/z/j3W759M7a (note the same lovely diagnostic note behavior).

It points at the same location, but at least it says "'rintf' is a builtin" (diag::note_previous_builtin_declaration). The new codepath specifically skips the code which emits that note.

This situation should now be improved in c7d4a05228090cb6b1b7f6e5d300f197897ac756.

If the declaration we're redeclaring is a builtin, should the diagnostic be in the "-Wincompatible-library-redeclaration" warning group? With this patch, we treat redefinitions of builtins without a prototype differently from redefinitions with a prototype, for example:

void acos() {} // error
void acos(void) {} // warning

Just ran into some code in Android which is using the first form.

If the declaration we're redeclaring is a builtin, should the diagnostic be in the "-Wincompatible-library-redeclaration" warning group? With this patch, we treat redefinitions of builtins without a prototype differently from redefinitions with a prototype, for example:

void acos() {} // error
void acos(void) {} // warning

Just ran into some code in Android which is using the first form.

Er, I keep going back and forth on it. My initial inclination is that this is closing a hole where we would incorrectly decide that these function signatures are compatible enough to merge together when that's not the case, so an error is appropriate. But the same can be said for redeclaring a builtin with an incorrect prototype rather than declaring it without any prototype. Given that these builtins are declared for the user automagically, I think I come down on this being a case we'd rather warn instead of err. It'd be weird to allow the user to define void printf(void) {} but not void printf(); (except in C2x mode).

If we make a change here, I think it'd be good to get it done for Clang 15. I'm not certain I've got the bandwidth to make this change in that timeframe though (I can hopefully start on this sometime this week, but I have prior commitments with deadlines that take priority).

If the declaration we're redeclaring is a builtin, should the diagnostic be in the "-Wincompatible-library-redeclaration" warning group? With this patch, we treat redefinitions of builtins without a prototype differently from redefinitions with a prototype, for example:

void acos() {} // error
void acos(void) {} // warning

Just ran into some code in Android which is using the first form.

Er, I keep going back and forth on it. My initial inclination is that this is closing a hole where we would incorrectly decide that these function signatures are compatible enough to merge together when that's not the case, so an error is appropriate. But the same can be said for redeclaring a builtin with an incorrect prototype rather than declaring it without any prototype. Given that these builtins are declared for the user automagically, I think I come down on this being a case we'd rather warn instead of err. It'd be weird to allow the user to define void printf(void) {} but not void printf(); (except in C2x mode).

If we make a change here, I think it'd be good to get it done for Clang 15. I'm not certain I've got the bandwidth to make this change in that timeframe though (I can hopefully start on this sometime this week, but I have prior commitments with deadlines that take priority).

I filed https://reviews.llvm.org/D131499 for this.