-
Notifications
You must be signed in to change notification settings - Fork 15k
[clang] Fix -Wuninitialized for values passed by const pointers #147221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
f1e26fe
d63b5dc
4ff02a7
d58b066
d3ca8c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -438,13 +438,10 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) { | |||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
| bool isTrivialBody = hasTrivialBody(CE); | ||||||||||||||||
| // If a value is passed by const pointer to a function, | ||||||||||||||||
| // we should not assume that it is initialized by the call, and we | ||||||||||||||||
| // conservatively do not assume that it is used. | ||||||||||||||||
| // If a value is passed by const reference to a function, | ||||||||||||||||
| // it should already be initialized. | ||||||||||||||||
| for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); | ||||||||||||||||
| I != E; ++I) { | ||||||||||||||||
| // A value passed by const pointer or reference to a function should already | ||||||||||||||||
| // be initialized. | ||||||||||||||||
| for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; | ||||||||||||||||
| ++I) { | ||||||||||||||||
| if ((*I)->isGLValue()) { | ||||||||||||||||
| if ((*I)->getType().isConstQualified()) | ||||||||||||||||
| classify((*I), isTrivialBody ? Ignore : ConstRefUse); | ||||||||||||||||
|
|
@@ -453,7 +450,7 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) { | |||||||||||||||
| const auto *UO = dyn_cast<UnaryOperator>(Ex); | ||||||||||||||||
| if (UO && UO->getOpcode() == UO_AddrOf) | ||||||||||||||||
| Ex = UO->getSubExpr(); | ||||||||||||||||
| classify(Ex, Ignore); | ||||||||||||||||
| classify(Ex, Use); | ||||||||||||||||
|
||||||||||||||||
| if (UO && UO->getOpcode() == UO_AddrOf) | |
| Ex = UO->getSubExpr(); | |
| classify(Ex, Ignore); | |
| classify(Ex, Use); | |
| if (UO && UO->getOpcode() == UO_AddrOf) { | |
| classify(UO->getSubExpr(), Ignore); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, rather than treating this case as Ignore, it seems best to treat it instead as ConstRefUse, since passing a local variable by const reference and passing its address by const pointer are analogous and should be treated the same way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestion does not cover the case:
void foo(const int *);
void test() {
int v;
foo(&v);
}
Note that gcc catches it: https://godbolt.org/z/TdKG35shz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, rather than treating this case as
Ignore, it seems best to treat it instead asConstRefUse, since passing a local variable by const reference and passing its address by const pointer are analogous and should be treated the same way.
TBH, I don't think we need a separate diagnostic for ConstRefUse while it can be reported as any other use of an uninitialized value. But changing that is not the purpose of this patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestion does not cover the case:
void foo(const int *); void test() { int v; foo(&v); }
Correct. We intentionally don't warn on that -- a "const reference use" is neither treated as initializing nor as using the local variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. We intentionally don't warn on that -- a "const reference use" is neither treated as initializing nor as using the local variable.
What is the basis of this intention?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning on such uses was found to lead to false positives. Such as all the cases you found in libc++'s test suite :)
The point is, for foo(&v), where foo takes a const pointer, we simply don't know what foo does to v. It might read from it, or it might just store a pointer to it somewhere. And -Wuninitialized / -Wsometimes-uninitialized are supposed to have zero false positives. What we do know is that foo does not initialize v, which is why we were marking it as ignore here (because otherwise we would treat a pointer to v as escaping, which conservatively marks it initialized).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With my suggested approach plus changing the kind from Ignore to ConstRefUse, we'd start warning under -Wuninitialized-const-reference (which is a subgroup of -Wuninitialized but should be a subgroup of -Wconditional-uninitialized, but that's a separate bug). That'd be consistent and probably reasonable, other than the warning message and flag being a bit inaccurate for this case, and wouldn't introduce false positives into the other uninitialized warning flags (that are supposed to not have false positives).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation. These two cases, passing an uninitialized value and passing the address of such a value, are indeed different, because the address is defined. I guess that implementing your second suggestion, i.e. to use ConstRefUse instead of Ignore, requires a lot more changes; at least, the warning message needs to be updated to reflect the actual case. I'll prepare a separate PR for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zygoloid ping. I've applied this suggestion in the last update. Could you take a look?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use
CE->arguments()in a range based for loop instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to do that to keep the fix as atomic as possible and to avoid concealing the actual behavior change among unrelated NFC changes.