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
RecursiveASTVisitor: Traverse the child of a BoundsValueExpr #978
Conversation
…per or ReplaceVariableHelper
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.
Some more details about the changes in this PR and the relevant parts of the bounds checker:
Problem Summary
The part of the bounds checker that detects free variables does the
following for expressions E1 and E2 that are involved in bounds expressions
D and S. (For example, E1 and E2 may be the base expressions of D and S.
If the declared bounds D are bounds(p, p + 1)
and the source bounds S are
bounds(q, q + 1)
, then E1 and E2 could be p
and q
, respectively.)
- Get the set of variables involved in E1. Let this set be Vars1.
- Get the set of variables involved in E2. Let this set be Vars2.
- For each variable V in Vars1:
- Check whether V is equal to an IntegerLiteral. If so, V is not a
free variable. - Check whether V is equal to some variable U in Vars2. If so, V is not
a free variable. - Check whether there is an indirect relationship between V and some variable
U in Vars2. If so, V is not a free variable. - Repeat steps 3-6, checking each variable in Vars2 against the set Vars1.
Steps 1 and 2 use a helper class called CollectVarSetHelper that returns the
set of all (unique) DeclRefExprs within an expression E. For example, if E is
(p * 2) + p + q + 3
, then CollectVarSetHelper will return the set { p, q }
for E.
CollectVarSetHelper extends RecursiveASTVisitor to traverse an expression E and
overrides the VisitDeclRefExpr
method to add a DeclRefExpr
to the set of
variables in E.
A BoundsValueExpr
has one child: a CHKCBindTemporaryExpr
, obtained by
calling E->getTemporaryBinding()
for a BoundsValueExpr
E
. By default,
RecursiveASTVisitor does not traverse E->getTemporaryBinding()
. Suppose we
have the following base expressions E1 and E2 for declared and inferred bounds:
- E1 =
a
- E2 =
BoundsValue(TempBinding(a))
CollectVarSetHelper will return { a }
for E1. However, for E2,
RecursiveASTVisitor will not traverse the TempBinding(a)
child of the
BoundsValue
. The VisitDeclRefExpr
method will never be called, and a
will never be added to the set of variables, so CollectVarSetHelper will
return {}
for E2. Then, when it is time to detect free variables between
the sets { a }
and {}
, a
will be treated as a free variable.
Solution: Modify Default Behavior of RecursiveASTVisitor
The fix for the above problem is to modify RecursiveASTVisitor so that, for
a BoundsValueExpr
E
, E->getTemporaryBinding()
is traversed by default.
This is consistent with the behavior of RecursiveASTVisitor for other kinds of
expressions, which is to fully traverse all children of the expression.
There are three classes in SemaBounds.cpp which use RecursiveASTVisitor which
need to override this behavior and not traverse the child of a BoundsValueExpr
.
NonModifyingExprSema
The NonModifyingExprSema class uses RecursiveASTVisitor to traverse an expression
E
to check whether E
contains any modifying expressions (e.g. assignments,
function calls, etc.). Any expressions that are wrapped in a BoundsValueExpr
should not be considered modifying, so NonModifyingExprSema should not traverse
the child of a BoundsValueExpr
. For example, consider:
array_ptr<int> getArr(void) : count(2);
void f(void) {
array_ptr<int> arr : count(2) = getArr();
}
The inferred bounds of arr
are bounds(BoundsValue(TempBinding(getArr())), BoundsValue(TempBinding(getArr())) + 2)
.
The getArr()
function call within the BoundsValue
expression should not be treated as a
modifying expression - if it is, this program will not compile. (Modifying expressions in bounds result in compile-time errors.)
VariableCountHelper
The UpdateAfterAssignment method in the bounds checker updates all observed
variable bounds after an assignment to a variable V
with an original value
OV
before the assignment. If V
appears in the observed bounds B
of a
variable W
, then the observed bounds of W
are set to B1
. B1
is the
result of replacing all occurrences of V
in B
with OV
.
The VariableCountHelper class uses RecursiveASTVisitor to traverse an expression
E
to determine the number of times a DeclRefExpr
V
appears in E
.
The ReplaceVariableHelper class uses TreeTransform to traverse an expression
E
to replace all occurrences of a DeclRefExpr
V
with an expression OV
.
If VariableCountHelper returns 0
for E
and V
, ReplaceVariableHelper returns
the original expression E
with no traversals and no modifications.
For expressions which contain BoundsValueExprs
, any variables within a
BoundsValueExpr
should not be replaced and should not count toward any
variable occurrence counts for the expression. For example, consider:
void f(array_ptr<int> arr : count(1)) {
arr = _Dynamic_bounds_cast<array_ptr<int>>(arr, count(2));
}
The inferred bounds of the RHS of the assignment are
bounds((array_ptr<int>)BoundsValue(TempBinding(arr)), (array_ptr<int>)BoundsValue(TempBinding(arr)) + 2)
,
where the (array_ptr<int>)
casts are bit casts.
The original value of arr
is (array_ptr<int>)arr
, where the (array_ptr<int>)
cast is a no-op cast.
The uses of arr
in the RHS bounds should not be replaced with the original value
(array_ptr<int>)arr
. The RHS bounds should remain unmodified.
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.
LGTM.
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.
LGTM, thanks!
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.
LGTM, thanks!
Fixes #974
Fixes #977
This PR modifies RecursiveASTVisitor to traverse the child (
E->getTemporaryBinding
) of aBoundsValueExpr
by default. This allows theCollectVarSetHelper
in SemaBounds.cpp to determine the correct set of variables involved in expressions that contain aBoundsValueExpr
. For example, in the expressionBoundsValue(TempBinding(x)) + y
,CollectVarSetHelper
will return the set{ x, y }
rather than{ y }
.Two other classes in SemaBounds.cpp that use RecursiveASTVisitor now override this behavior so that they do not traverse the temporary binding of a
BoundsValueExpr
:ReplaceVariable(E, V, OriginalValue)
returns the original expressionE
if VariableCountHelper returns 0 as the variable occurrence count ofV
inE
. IfV
occurs within a BoundsValueExpr withinE
, then ReplaceVariable should returnE
without traversing it. Therefore, for an expression such asBoundsValue(TempBinding(x))
, VariableCountHelper should return 0 as the occurrence count ofx
.Testing: