Skip to content

Commit 8fb96d9

Browse files
authored
Fix compiler assert about bounds expression already existing. (#537)
The children() method for iterating over chidren of AST cast expressions was incorrectly including compiler-generated bounds expressions. Child AST nodes should be nodes that appear in the source program and additional information shouldn't be treated as child nodes. There were complex IR invariants about when a bounds expression stored within a cast expression was child AST node or not. This change fixes the bug and simplifies the AST invariants. This fixes issue #526. for cast expressions, there is now one entry for bounds expressions declared as part of the program. There are separate nodes for normalized bounds and inferred bounds. Testing: - Added a new regression test case for the failing case. - Passes existing Checked C and clang Checked C tests.
1 parent aba5f72 commit 8fb96d9

File tree

6 files changed

+68
-40
lines changed

6 files changed

+68
-40
lines changed

include/clang/AST/Expr.h

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2958,7 +2958,8 @@ class BoundsExpr : public Expr {
29582958
/// classes).
29592959
class CastExpr : public Expr {
29602960
private:
2961-
// BOUNDS - declared bounds of the result of the cast expression
2961+
// BOUNDS - declared bounds of a bounds cast expression. Null
2962+
// for other cast expressions.
29622963
// NORMALIZED_BOUNDS - normalized version of declared bounds.
29632964
// SUBEXPRBOUNDS - inferred bounds of subexpression
29642965
enum { OP, BOUNDS, NORMALIZED_BOUNDS, SUBEXPRBOUNDS, END_EXPR = 4 };
@@ -3067,41 +3068,33 @@ class CastExpr : public Expr {
30673068
// Iterators
30683069

30693070
child_range children() {
3070-
// For bounds cast expression, the bounds are included as a subexpression
3071-
// in the AST because they are part of the source program. For other
3072-
// cast expressions, the bounds are not included because they are
3073-
// inferred by the compiler only when needed.
3071+
// For a bounds cast expression, the declared bounds are included as a child
3072+
// expression because they are part of the source program. Other cast expressions
3073+
// will not have declared bounds. We do not include the other bounds expressions
3074+
// in children because they are constructed by the compiler.
30743075
if (getStmtClass() == BoundsCastExprClass)
3075-
return child_range(&SubExprs[OP], &SubExprs[END_EXPR]);
3076+
return child_range(&SubExprs[OP], &SubExprs[BOUNDS] + 1);
30763077
else
30773078
return child_range(&SubExprs[OP], &SubExprs[OP] + 1);
30783079
}
30793080

30803081
const_child_range children() const {
3081-
// For bounds cast expression, the bounds are included as a subexpression
3082-
// in the AST because they are part of the source program. For other
3083-
// cast expressions, the bounds are not included because they are
3084-
// inferred by the compiler only when needed.
30853082
if (getStmtClass() == BoundsCastExprClass)
3086-
return const_child_range(&SubExprs[OP], &SubExprs[END_EXPR]);
3083+
return const_child_range(&SubExprs[OP], &SubExprs[BOUNDS] + 1);
30873084
else
30883085
return const_child_range(&SubExprs[OP], &SubExprs[OP] + 1);
30893086
}
30903087

30913088
// Checked C bounds information
30923089

30933090
/// \brief Return true if the cast expression has a bounds expression
3094-
/// associated with it. Depending on the type of the cast expression,
3095-
/// the bounds expression may be declared as part of the program or inferred
3096-
/// by the compiler.
3091+
/// declared as part of it. This should only be true for bounds cast
3092+
/// expressions.
30973093
bool hasBoundsExpr() const { return SubExprs[BOUNDS] != nullptr; }
30983094

30993095
/// \brief Returns the bounds associated with the cast expression, if any.
31003096
/// * If the cast expression is a BoundsCast expression, it is the bounds
31013097
/// declared for the expression.
3102-
/// * If the cast expression is a cast to Ptr, it is the compiler-inferred
3103-
/// bounds of the subexpression of the cast expression. The bounds must be
3104-
/// provably large enough at compile-time to contain a value of the _Ptr type.
31053098
BoundsExpr *getBoundsExpr() {
31063099
return cast_or_null<BoundsExpr>(SubExprs[BOUNDS]);
31073100
}
@@ -3116,7 +3109,7 @@ class CastExpr : public Expr {
31163109

31173110
bool hasNormalizedBoundsExpr() const { return SubExprs[NORMALIZED_BOUNDS] != nullptr; }
31183111

3119-
// \brief Normalized version of bounds associated with the cast expression
3112+
// \brief Normalized version of declared bounds for the cast expression
31203113
// (getBoundsExpr).
31213114
BoundsExpr *getNormalizedBoundsExpr() {
31223115
return cast_or_null<BoundsExpr>(SubExprs[NORMALIZED_BOUNDS]);
@@ -3129,7 +3122,8 @@ class CastExpr : public Expr {
31293122
SubExprs[NORMALIZED_BOUNDS] = E;
31303123
}
31313124

3132-
// The inferred bounds of the subexpression of the cast expression.
3125+
// The inferred bounds of the subexpression of the cast expression. This is used
3126+
// for dynamic bounds cast. It is also used for implicit or C-style casts Ptr types.
31333127
bool hasSubExprBoundsExpr() const { return SubExprs[SUBEXPRBOUNDS] != nullptr; }
31343128
BoundsExpr *getSubExprBoundsExpr() {
31353129
return cast_or_null<BoundsExpr>(SubExprs[SUBEXPRBOUNDS]);

lib/AST/ASTDumper.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,13 +2182,17 @@ void ASTDumper::VisitCastExpr(const CastExpr *Node) {
21822182
if (Node->isBoundsSafeInterface())
21832183
OS << " BoundsSafeInterface";
21842184

2185-
if (Node->getStmtClass() != Expr::BoundsCastExprClass)
2186-
if (const BoundsExpr *Bounds = Node->getBoundsExpr()) {
2187-
dumpChild([=] {
2188-
OS << "Inferred Bounds";
2189-
dumpStmt(Bounds);
2190-
});
2191-
}
2185+
if (const BoundsExpr *NormalizedBounds = Node->getNormalizedBoundsExpr())
2186+
dumpChild([=] {
2187+
OS << "Normalized Bounds";
2188+
dumpStmt(NormalizedBounds);
2189+
});
2190+
2191+
if (const BoundsExpr *SubExprBounds = Node->getSubExprBoundsExpr())
2192+
dumpChild([=] {
2193+
OS << "Inferred SubExpr Bounds";
2194+
dumpStmt(SubExprBounds);
2195+
});
21922196
}
21932197

21942198
void ASTDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {

lib/Sema/SemaBounds.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,7 @@ namespace {
22852285
#if TRACE_CFG
22862286
llvm::outs() << "Visiting ";
22872287
S->dump(llvm::outs());
2288+
llvm::outs().flush();
22882289
#endif
22892290
TraverseStmt(S, IsChecked);
22902291
}
@@ -2566,22 +2567,22 @@ namespace {
25662567
!E->getType()->isFunctionPointerType()) {
25672568
bool IncludeNullTerminator =
25682569
E->getType()->getPointeeOrArrayElementType()->isNtCheckedArrayType();
2569-
BoundsExpr *SrcBounds =
2570+
BoundsExpr *SubExprBounds =
25702571
S.InferRValueBounds(E->getSubExpr(), IncludeNullTerminator);
2571-
if (SrcBounds->isUnknown()) {
2572+
if (SubExprBounds->isUnknown()) {
25722573
S.Diag(E->getSubExpr()->getLocStart(),
25732574
diag::err_expected_bounds_for_ptr_cast)
25742575
<< E->getSubExpr()->getSourceRange();
2575-
SrcBounds = S.CreateInvalidBoundsExpr();
2576+
SubExprBounds = S.CreateInvalidBoundsExpr();
25762577
} else {
25772578
BoundsExpr *TargetBounds =
25782579
S.CreateTypeBasedBounds(E, E->getType(), false, false);
25792580
CheckBoundsDeclAtStaticPtrCast(E, TargetBounds, E->getSubExpr(),
2580-
SrcBounds, InCheckedScope);
2581+
SubExprBounds, InCheckedScope);
25812582
}
2582-
assert(SrcBounds);
2583-
assert(!E->getBoundsExpr());
2584-
E->setBoundsExpr(SrcBounds);
2583+
assert(SubExprBounds);
2584+
assert(!E->getSubExprBoundsExpr());
2585+
E->setSubExprBoundsExpr(SubExprBounds);
25852586

25862587
if (DumpBounds)
25872588
DumpExpression(llvm::outs(), E);

test/CheckedC/inferred-bounds/member-reference.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ _Checked void f15(struct Interop_S4 a1) {
448448
}
449449

450450
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
451-
// CHECK: |-Inferred Bounds
451+
// CHECK: |-Inferred SubExpr Bounds
452452
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
453453
// CHECK: | |-ImplicitCastExpr {{0x[0-9a-f]+}} '_Array_ptr<int>':'_Array_ptr<int>' <ArrayToPointerDecay>
454454
// CHECK: | | `-DeclRefExpr {{0x[0-9a-f]+}} 'int _Checked[5]' lvalue Var {{0x[0-9a-f]+}} 'global_arr2' 'int _Checked[5]'

test/CheckedC/inferred-bounds/ptr-cast.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void f1(void) {
2424
_Ptr<int> p = &x;
2525

2626
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
27-
// CHECK: |-Inferred Bounds
27+
// CHECK: |-Inferred SubExpr Bounds
2828
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
2929
// CHECK: | |-UnaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' prefix '&'
3030
// CHECK: | | `-DeclRefExpr {{0x[0-9a-f]+}} 'int' lvalue Var {{0x[0-9a-f]+}} 'x' 'int'
@@ -38,7 +38,7 @@ void f1(void) {
3838
p = &y;
3939

4040
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
41-
// CHECK: |-Inferred Bounds
41+
// CHECK: |-Inferred SubExpr Bounds
4242
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
4343
// CHECK: | |-UnaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' prefix '&'
4444
// CHECK: | | `-DeclRefExpr {{0x[0-9a-f]+}} 'int' lvalue Var {{0x[0-9a-f]+}} 'y' 'int'
@@ -63,7 +63,7 @@ void f2(_Array_ptr<int> p : count(5)) {
6363
_Ptr<int> x = &p[2];
6464

6565
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
66-
// CHECK: |-Inferred Bounds
66+
// CHECK: |-Inferred SubExpr Bounds
6767
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
6868
// CHECK: | |-ImplicitCastExpr {{0x[0-9a-f]+}} '_Array_ptr<int>' <LValueToRValue>
6969
// CHECK: | | `-DeclRefExpr {{0x[0-9a-f]+}} '_Array_ptr<int>' lvalue ParmVar {{0x[0-9a-f]+}} 'p' '_Array_ptr<int>'
@@ -93,7 +93,7 @@ void f3(void) {
9393
_Ptr<int> p = &v.f;
9494

9595
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
96-
// CHECK: |-Inferred Bounds
96+
// CHECK: |-Inferred SubExpr Bounds
9797
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
9898
// CHECK: | |-UnaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' prefix '&'
9999
// CHECK: | | `-MemberExpr {{0x[0-9a-f]+}} 'int' lvalue .f {{0x[0-9a-f]+}}
@@ -117,7 +117,7 @@ void f4(struct S b _Checked[9]) {
117117
_Ptr<int> p = &((*b).f);
118118

119119
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
120-
// CHECK: |-Inferred Bounds
120+
// CHECK: |-Inferred SubExpr Bounds
121121
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
122122
// CHECK: | |-UnaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' prefix '&'
123123
// CHECK: | | `-MemberExpr {{0x[0-9a-f]+}} 'int' lvalue .f {{0x[0-9a-f]+}}
@@ -151,7 +151,7 @@ void f5(struct S arr _Checked[][12] : count(len), int i, int j, int len) {
151151
p = &(arr[i][j].f);
152152

153153
// CHECK: ImplicitCastExpr {{0x[0-9a-f]+}} '_Ptr<int>' <BitCast>
154-
// CHECK: |-Inferred Bounds
154+
// CHECK: |-Inferred SubExpr Bounds
155155
// CHECK: | `-RangeBoundsExpr {{0x[0-9a-f]+}} 'NULL TYPE'
156156
// CHECK: | |-UnaryOperator {{0x[0-9a-f]+}} '_Array_ptr<int>' prefix '&'
157157
// CHECK: | | `-MemberExpr {{0x[0-9a-f]+}} 'int' lvalue .f {{0x[0-9a-f]+}}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// This is a regression test case for
3+
// https://github.com/Microsoft/checkedc-clang/issues/526
4+
//
5+
// The compiler was crashing with an internal assertion that an expression
6+
// to which a bounds check was going to be added already had a bounds
7+
// check.
8+
//
9+
// The problem is that the compiler was traversing the 1st argument of a
10+
// _Dynamic_bounds_cast operation twice. The compiler inferred bounds
11+
// that use the 1st argument. It then attached them to the AST. There
12+
// was a lack of clarity in the IR and the inferred bounds were also
13+
// traversed, causing the assert.
14+
//
15+
// RUN: %clang -cc1 -verify %s
16+
// expected-no-diagnostics
17+
18+
struct obj {
19+
_Array_ptr<_Nt_array_ptr<char>> names : count(len);
20+
unsigned int len;
21+
};
22+
23+
void f(const struct obj *object ) {
24+
unsigned int i = 0;
25+
_Nt_array_ptr<const char> t : count(0) =
26+
_Dynamic_bounds_cast<_Nt_array_ptr<const char>>(object->names[i],
27+
count(0));
28+
}
29+

0 commit comments

Comments
 (0)