Skip to content

Commit 4b47004

Browse files
authored
Target a single assignment in bounds checking error messages (#893)
* Target error message at assignments * Target subexpressions to blame on error messages for unknown inferred bounds * Update tests to reflect fine-grained error messages * Update comments * Remove unused variable * * Also add (V, E) to BlameAssignments if E modifies the bounds of V * Simplify logic and improve comments * Update tests to reflect the missing case * Restore whitespace * Restore whitespace * Restore whitespace * Restore whitespace * Refactor and address the missing case; fix tests * Clarify the logic in BlameAssignmentWithinStmt * Reorder BoundsDeclarationCheck enum and update comments * Add comments and resolve conflict
1 parent 39659b3 commit 4b47004

File tree

13 files changed

+202
-131
lines changed

13 files changed

+202
-131
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10164,20 +10164,21 @@ def err_bounds_type_annotation_lost_checking : Error<
1016410164

1016510165
def warn_bounds_declaration_invalid : Warning<
1016610166
"cannot prove declared bounds for %1 are valid after "
10167-
"%select{assignment|initialization|statement}0">,
10167+
"%select{assignment|decrement|increment|initialization|statement}0">,
1016810168
InGroup<CheckBoundsDeclsUnchecked>;
1016910169

1017010170
def warn_checked_scope_bounds_declaration_invalid : Warning<
1017110171
"cannot prove declared bounds for %1 are valid after "
10172-
"%select{assignment|initialization|statement}0">,
10172+
"%select{assignment|decrement|increment|initialization|statement}0">,
1017310173
InGroup<CheckBoundsDeclsChecked>;
1017410174

1017510175
def error_bounds_declaration_invalid : Error<
1017610176
"declared bounds for %1 are invalid after "
10177-
"%select{assignment|initialization|statement}0">;
10177+
"%select{assignment|decrement|increment|initialization|statement}0">;
1017810178

1017910179
def err_unknown_inferred_bounds : Error<
10180-
"inferred bounds for %0 are unknown after statement">;
10180+
"inferred bounds for %1 are unknown after "
10181+
"%select{assignment|decrement|increment|initialization|statement}0">;
1018110182

1018210183
def note_declared_bounds : Note<
1018310184
"(expanded) declared bounds are '%0'">;

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5186,8 +5186,10 @@ class Sema {
51865186

51875187
enum BoundsDeclarationCheck {
51885188
BDC_Assignment,
5189+
BDC_Decrement,
5190+
BDC_Increment,
51895191
BDC_Initialization,
5190-
BDC_Statement
5192+
BDC_Statement,
51915193
};
51925194

51935195
/// \brief Check that address=of operation is not taking the

clang/lib/Sema/SemaBounds.cpp

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,21 @@ namespace {
705705
// user when diagnosing unknown bounds errors.
706706
llvm::DenseMap<const VarDecl *, SmallVector<Expr *, 4>> UnknownSrcBounds;
707707

708+
// BlameAssignments maps a variable declaration V to an expression in a
709+
// top-level CFG statement that last updates any variable used in the
710+
// declared bounds of V.
711+
//
712+
// BlameAssignments is used to provide more context for two types of
713+
// diagnostic messages:
714+
// 1. The compiler cannot prove or can disprove the declared bounds for
715+
// V are valid after an assignment to a variable in the bounds of V; and
716+
// 2. The inferred bounds of V become unknown after an assignment to a
717+
// variable in the bounds of V.
718+
//
719+
// BlameAssignments is updated in UpdateAfterAssignments and reset after
720+
// checking each top-level CFG statement.
721+
llvm::DenseMap<const VarDecl *, Expr *> BlameAssignments;
722+
708723
// TargetSrcEquality maps a target expression V to the most recent
709724
// expression Src that has been assigned to V within the current
710725
// top-level CFG statement. When validating the bounds context,
@@ -718,6 +733,7 @@ namespace {
718733
SameValue.clear();
719734
LostVariables.clear();
720735
UnknownSrcBounds.clear();
736+
BlameAssignments.clear();
721737
TargetSrcEquality.clear();
722738
}
723739
};
@@ -2819,8 +2835,8 @@ namespace {
28192835
// Update the checking state and result bounds for assignments to `e1`
28202836
// where `e1` is a variable.
28212837
if (LHSVar)
2822-
ResultBounds = UpdateAfterAssignment(LHSVar, Target, Src, ResultBounds,
2823-
CSS, State, State);
2838+
ResultBounds = UpdateAfterAssignment(LHSVar, E, Target, Src,
2839+
ResultBounds, CSS, State, State);
28242840
// Update EquivExprs and SameValue for assignments where `e1` is not
28252841
// a variable.
28262842
else
@@ -3265,8 +3281,8 @@ namespace {
32653281
BoundsExpr *RHSBounds = RValueCastBounds(Target, SubExprTargetBounds,
32663282
SubExprLValueBounds,
32673283
SubExprBounds, State);
3268-
IncDecResultBounds = UpdateAfterAssignment(V, Target, RHS, RHSBounds,
3269-
CSS, State, State);
3284+
IncDecResultBounds = UpdateAfterAssignment(
3285+
V, E, Target, RHS, RHSBounds, CSS, State, State);
32703286
}
32713287

32723288
// Update the set SameValue of expressions that produce the same
@@ -3927,13 +3943,13 @@ namespace {
39273943
// whose observed bounds are unknown after checking the top-level CFG
39283944
// statement St.
39293945
//
3930-
// State contians information that is used to provide more context in
3946+
// State contains information that is used to provide more context in
39313947
// the diagnostic messages.
39323948
void DiagnoseUnknownObservedBounds(Stmt *St, const VarDecl *V,
39333949
BoundsExpr *DeclaredBounds,
39343950
CheckingState State) {
3935-
S.Diag(St->getBeginLoc(), diag::err_unknown_inferred_bounds)
3936-
<< V << St->getSourceRange();
3951+
BlameAssignmentWithinStmt(St, V, State,
3952+
diag::err_unknown_inferred_bounds);
39373953
S.Diag(V->getLocation(), diag::note_declared_bounds)
39383954
<< DeclaredBounds << DeclaredBounds->getSourceRange();
39393955

@@ -4000,22 +4016,12 @@ namespace {
40004016
return;
40014017
}
40024018

4003-
// For a declaration, the diagnostic message should start at the
4004-
// location of v rather than the beginning of St. If the message
4005-
// starts at the beginning of a declaration T v = e, then extra
4006-
// diagnostics may be emitted for T.
4007-
SourceLocation Loc = St->getBeginLoc();
4008-
if (isa<DeclStmt>(St))
4009-
Loc = V->getLocation();
4010-
40114019
unsigned DiagId = (Result == ProofResult::False) ?
40124020
diag::error_bounds_declaration_invalid :
40134021
(CSS != CheckedScopeSpecifier::CSS_Unchecked?
40144022
diag::warn_checked_scope_bounds_declaration_invalid :
40154023
diag::warn_bounds_declaration_invalid);
4016-
S.Diag(Loc, DiagId)
4017-
<< Sema::BoundsDeclarationCheck::BDC_Statement << V
4018-
<< St->getSourceRange() << St->getSourceRange();
4024+
SourceLocation Loc = BlameAssignmentWithinStmt(St, V, State, DiagId);
40194025
if (Result == ProofResult::False)
40204026
ExplainProofFailure(Loc, Cause, ProofStmtKind::BoundsDeclaration);
40214027
S.Diag(V->getLocation(), diag::note_declared_bounds)
@@ -4024,11 +4030,62 @@ namespace {
40244030
<< ObservedBounds << ObservedBounds->getSourceRange();
40254031
}
40264032

4033+
// BlameAssignmentWithinStmt prints a diagnostic message that highlights the
4034+
// assignment expression in St that causes V's observed bounds to be unknown
4035+
// or not provably valid. If St is a DeclStmt, St itself and V are
4036+
// highlighted. BlameAssignmentWithinStmt returns the source location of
4037+
// the blamed assignment.
4038+
SourceLocation BlameAssignmentWithinStmt(Stmt *St, const VarDecl *V,
4039+
CheckingState State,
4040+
unsigned DiagId) const {
4041+
assert(St);
4042+
SourceRange SrcRange = St->getSourceRange();
4043+
auto BDCType = Sema::BoundsDeclarationCheck::BDC_Statement;
4044+
4045+
// For a declaration, show the diagnostic message that starts at the
4046+
// location of v rather than the beginning of St and return. If the
4047+
// message starts at the beginning of a declaration T v = e, then extra
4048+
// diagnostics may be emitted for T.
4049+
SourceLocation Loc = St->getBeginLoc();
4050+
if (isa<DeclStmt>(St)) {
4051+
Loc = V->getLocation();
4052+
BDCType = Sema::BoundsDeclarationCheck::BDC_Initialization;
4053+
S.Diag(Loc, DiagId) << BDCType << V << SrcRange << SrcRange;
4054+
return Loc;
4055+
}
4056+
4057+
// If not a declaration, find the assignment (if it exists) in St to blame
4058+
// for the error or warning.
4059+
auto It = State.BlameAssignments.find(V);
4060+
if (It != State.BlameAssignments.end()) {
4061+
Expr *BlameExpr = It->second;
4062+
Loc = BlameExpr->getBeginLoc();
4063+
SrcRange = BlameExpr->getSourceRange();
4064+
4065+
// Choose the type of assignment E to show in the diagnostic messages
4066+
// from: assignment (=), decrement (--) or increment (++). If none of
4067+
// these cases match, the diagnostic message reports that the error is
4068+
// for a statement.
4069+
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BlameExpr)) {
4070+
if (UO->isIncrementOp())
4071+
BDCType = Sema::BoundsDeclarationCheck::BDC_Increment;
4072+
else if (UO->isDecrementOp())
4073+
BDCType = Sema::BoundsDeclarationCheck::BDC_Decrement;
4074+
} else if (isa<BinaryOperator>(BlameExpr)) {
4075+
// Must be an assignment or a compound assignment, because E is
4076+
// modifying.
4077+
BDCType = Sema::BoundsDeclarationCheck::BDC_Assignment;
4078+
}
4079+
}
4080+
S.Diag(Loc, DiagId) << BDCType << V << SrcRange << SrcRange;
4081+
return Loc;
4082+
}
4083+
40274084
// Methods to update the checking state.
40284085

40294086
// UpdateAfterAssignment updates the checking state after a variable V
4030-
// is updated in an assignment Target = Src, based on the state before
4031-
// the assignment. It also returns updated bounds for Src.
4087+
// is updated in an assignment E of the form Target = Src, based on the
4088+
// state before the assignment. It also returns updated bounds for Src.
40324089
//
40334090
// If V has an original value, the original value is substituted for
40344091
// any uses of the value of V in the bounds in ObservedBounds and the
@@ -4041,8 +4098,8 @@ namespace {
40414098
// SrcBounds are the original bounds for the source of the assignment.
40424099
//
40434100
// PrevState is the checking state that was true before the assignment.
4044-
BoundsExpr *UpdateAfterAssignment(DeclRefExpr *V, Expr *Target, Expr *Src,
4045-
BoundsExpr *SrcBounds,
4101+
BoundsExpr *UpdateAfterAssignment(DeclRefExpr *V, Expr *E, Expr *Target,
4102+
Expr *Src, BoundsExpr *SrcBounds,
40464103
CheckedScopeSpecifier CSS,
40474104
const CheckingState PrevState,
40484105
CheckingState &State) {
@@ -4078,6 +4135,13 @@ namespace {
40784135
BoundsExpr *AdjustedBounds = ReplaceVariableInBounds(Bounds, V, OriginalValue, CSS);
40794136
if (!Pair.second->isUnknown() && AdjustedBounds->isUnknown())
40804137
State.LostVariables[W] = std::make_pair(Bounds, V);
4138+
4139+
// If E modifies the bounds of W, add the pair to BlameAssignments. We
4140+
// can check this cheaply by comparing the pointer values of
4141+
// AdjustedBounds and Bounds because ReplaceVariableInBounds returns
4142+
// Bounds as AdjustedBounds if Bounds is not adjusted.
4143+
if (AdjustedBounds != Bounds)
4144+
State.BlameAssignments[W] = E;
40814145
State.ObservedBounds[W] = AdjustedBounds;
40824146
}
40834147

@@ -4087,6 +4151,10 @@ namespace {
40874151
if (DeclaredBounds)
40884152
State.ObservedBounds[VariableDecl] = AdjustedSrcBounds;
40894153

4154+
// Record that E updates the observed bounds of VariableDecl.
4155+
if (DeclaredBounds)
4156+
State.BlameAssignments[VariableDecl] = E;
4157+
40904158
// If the initial source bounds were not unknown, but they are unknown
40914159
// after replacing uses of V, then the assignment to V caused the
40924160
// source bounds (which are the observed bounds for V) to be unknown.

clang/test/CheckedC/inferred-bounds/basic.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ void f5(void) {
131131
// CHECK: NullaryBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE' Any
132132

133133
void f6(_Array_ptr<int> a : bounds(a, a + 5)) {
134-
a = (_Array_ptr<int>) 5; // expected-error {{inferred bounds for 'a' are unknown after statement}}
134+
a = (_Array_ptr<int>) 5; // expected-error {{inferred bounds for 'a' are unknown after assignment}}
135135
}
136136

137137
// CHECK: BinaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' '='
@@ -150,7 +150,7 @@ void f6(_Array_ptr<int> a : bounds(a, a + 5)) {
150150
// CHECK: NullaryBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE' Invalid
151151

152152
void f7(void) {
153-
_Array_ptr<int> d : count(5) = (_Array_ptr<int>) 5; // expected-error {{inferred bounds for 'd' are unknown after statement}}
153+
_Array_ptr<int> d : count(5) = (_Array_ptr<int>) 5; // expected-error {{inferred bounds for 'd' are unknown after initialization}}
154154
}
155155

156156
// CHECK: VarDecl {{0x[0-9a-f]+}} {{.*}} d '_Array_ptr<int>' cinit
@@ -190,7 +190,7 @@ void f8(_Array_ptr<int> a, _Array_ptr<int> b : count(5)) {
190190
// CHECK: `-IntegerLiteral {{0x[0-9a-f]+}} 'int' 5
191191

192192
void f9(int a) {
193-
_Array_ptr<int> b : count(5) = (_Array_ptr<int>) !a; // expected-error {{inferred bounds for 'b' are unknown after statement}}
193+
_Array_ptr<int> b : count(5) = (_Array_ptr<int>) !a; // expected-error {{inferred bounds for 'b' are unknown after initialization}}
194194
}
195195

196196
// CHECK: VarDecl {{0x[0-9a-f]+}} {{.*}} b '_Array_ptr<int>' cinit
@@ -207,7 +207,7 @@ void f9(int a) {
207207
// CHECK: NullaryBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE' Invalid
208208

209209
void f10(float a) {
210-
_Array_ptr<int> b : count(5) = (_Array_ptr<int>)((int)a); // expected-error {{inferred bounds for 'b' are unknown after statement}}
210+
_Array_ptr<int> b : count(5) = (_Array_ptr<int>)((int)a); // expected-error {{inferred bounds for 'b' are unknown after initialization}}
211211
}
212212

213213
// CHECK: VarDecl {{0x[0-9a-f]+}} {{.*}} b '_Array_ptr<int>' cinit
@@ -260,7 +260,7 @@ void f20(_Array_ptr<int> a : count(len),
260260

261261
void f21(_Array_ptr<int> a : count(5),
262262
_Array_ptr<int> b) {
263-
a = b; // expected-error {{inferred bounds for 'a' are unknown after statement}}
263+
a = b; // expected-error {{inferred bounds for 'a' are unknown after assignment}}
264264
}
265265

266266
// CHECK: BinaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' '='
@@ -280,7 +280,7 @@ void f21(_Array_ptr<int> a : count(5),
280280

281281
// Only test declarations for the negative case (where an error is expected}
282282
void f22(_Array_ptr<int> b) {
283-
_Array_ptr<int> a : count(5) = b; // expected-error {{inferred bounds for 'a' are unknown after statement}}
283+
_Array_ptr<int> a : count(5) = b; // expected-error {{inferred bounds for 'a' are unknown after initialization}}
284284
}
285285

286286
// CHECK: VarDecl {{0x[0-9a-f]+}} {{.*}} a '_Array_ptr<int>' cinit

0 commit comments

Comments
 (0)