-
Notifications
You must be signed in to change notification settings - Fork 79
Target a single assignment in bounds checking error messages #893
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
Conversation
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.
Looks good so far! I think there is a missing case where the compiler cannot prove (or can disprove) that the updated observed bounds imply the declared bounds of a variable v
after an assignment to a variable i
that is used in the declared bounds of v
. Also, please remove extraneous whitespace changes from the PR.
clang/lib/Sema/SemaBounds.cpp
Outdated
BDCType = Sema::BoundsDeclarationCheck::BDC_Initialization; | ||
} | ||
|
||
// Find the last assignment to V to blame in the diagnostic message if it |
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.
The expression in BlameAssignments (if it exists) might not be an assignment to V. It could be an assignment to a variable used in the bounds of V.
clang/lib/Sema/SemaBounds.cpp
Outdated
@@ -4069,8 +4134,10 @@ namespace { | |||
const VarDecl *W = Pair.first; | |||
BoundsExpr *Bounds = Pair.second; | |||
BoundsExpr *AdjustedBounds = ReplaceVariableInBounds(Bounds, V, OriginalValue, CSS); | |||
if (!Pair.second->isUnknown() && AdjustedBounds->isUnknown()) | |||
if (!Pair.second->isUnknown() && AdjustedBounds->isUnknown()) { |
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.
This seems to only update BlameAssignments if the updated observed bounds of W are unknown. For a case like:
unsigned int i;
array_ptr<int> p : count(i) = 0;
// Original value of i in i++ is i - 1
// i++ causes the updated observed bounds of p to be (p, p + i - 1)
i++, unrelated_variable = 0;
We want BlameAssignments[p] => i++
.
// | ||
// BlameAssignments is used to provide more context for two types of | ||
// diagnostic messages: | ||
// 1. The compiler cannot prove or can disprove the declared bounds for |
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.
It is also possible that the compiler cannot prove or can disprove the declared bounds for V are valid after an assignment to a variable used in the declared bounds of V. For example:
unsigned int len;
array_ptr<int> p : count(len) = 0;
// Original value of len is len + 2
// Updated observed bounds of p are (p, p + len - 2)
// Cannot prove that bounds(p, p + len - 2) imply (p, p + len)
len = len + 2;
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've changed the comment to this general case.
* Simplify logic and improve comments
} | ||
|
||
// Find the assignment (if it exists) to blame for the error or warning. | ||
auto It = State.BlameAssignments.find(V); |
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.
Can this logic move into a helper method that sets a SourceLocation, SourceRange, and BDCType based on St
and BlameAssignments
?
clang/lib/Sema/SemaBounds.cpp
Outdated
// the whole St in the error message. | ||
SourceLocation Loc = St->getBeginLoc(); | ||
SourceRange SrcRange = St->getSourceRange(); | ||
auto BDCType = Sema::BoundsDeclarationCheck::BDC_Statement; |
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.
This could miss the case where the bounds of V
are unknown after an initialization, e.g.
array_ptr<int> unknown_src : bounds(unknown);
// Bounds of arr are unknown after initialization
array_ptr<int> arr : count(1) = unknown_src;
clang/lib/Sema/SemaBounds.cpp
Outdated
// BlameAssignmentWithinStmt returns the source location of the blamed | ||
// assignment. | ||
SourceLocation BlameAssignmentWithinStmt(Stmt *St, | ||
const VarDecl *V, CheckingState State, unsigned DiagId) const { |
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.
Please align const VarDecl *V
underneath Stmt *St
(and ensure the remaining parameters are on separate lines if necessary in order to prevent lines longer than 80 characters).
clang/lib/Sema/SemaBounds.cpp
Outdated
@@ -4017,11 +4023,55 @@ namespace { | |||
<< ObservedBounds << ObservedBounds->getSourceRange(); | |||
} | |||
|
|||
// BlameAssignmentWithinStmt prints a diagnostic message that highlights the | |||
// assignment expression in St that causes V's observed bounds to be unknown | |||
// or invalid. If St is a DeclStmt, St itself and V are highlighted. |
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.
Nit: BlameAssignmentWithinStmt can also be used to print a diagnostic message if V's bounds are not necessarily invalid, but are not provably valid. I suggest updating the comment to something like "not provably valid" instead of "invalid".
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.
Thanks for the suggestion!
// starts at the beginning of a declaration T v = e, then extra | ||
// diagnostics may be emitted for T. | ||
SourceLocation Loc = St->getBeginLoc(); | ||
if (isa<DeclStmt>(St)) { |
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.
Is is possible for a DeclStmt
to have an entry in State.BlameAssignments
? If not, you can early return here rather than searching for V
in BlameAssignments
below.
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.
DeclStmt
does not have entries in State.BlameAssignments
. I made the function return early inside the if block.
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.
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.
Overall looks fine to me. Just a couple of clarifying comments.
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BlameExpr)) { | ||
if (UO->isIncrementOp()) | ||
BDCType = Sema::BoundsDeclarationCheck::BDC_Increment; | ||
else if (UO->isDecrementOp()) |
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.
Are there plans to handle other unary operators like unary minus
, deref
, address-of
, etc?
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.
No. Here UO
must be a self-modifying unary operator (because UO causes the bounds to be updated), and I can only think of ++
and --
.
I can also put an assert here.
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.
Looks great. Thank you! I have one suggestion for improving a comment.
clang/lib/Sema/SemaBounds.cpp
Outdated
|
||
// Choose the type of assignment E to show in the diagnostic messages | ||
// from: assignment (=), decrement (--) or increment (++). These are all | ||
// modifying operators that can update observed bounds. |
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 you add a comment that says "If none of these cases match, the diagnostic message reports that the error is for a statement."? When I first read the code, I noticed there were several ways we could fall off the end of an if-statement chain. It doesn't matter because the code just reports a diagnostic message for a statement type.
An example
Error message before the change (highlight the whole statement):
After the change (blame the embedded assignment that caused the error; print 'assignment' instead of 'statement'):