[cfe-dev] fix for Clang PR 8419
Zhanyong Wan (λx.x x)
wan at google.com
Fri Nov 19 17:24:05 PST 2010
Hi Ted,
Thanks for the quick feedback! I'll respond to your comments about
the code comments later, as they are less important.
On Fri, Nov 19, 2010 at 2:09 PM, Ted Kremenek <kremenek at apple.com> wrote:
> On Nov 19, 2010, at 10:38 AM, Zhanyong Wan (λx.x x) wrote:
>
> I think the motivation behind this change is right, but not the change
> itself.
> The problem with VisitChildren() is that it has no context of whether
> 'Terminator' (which is misnamed) expects the subexpression to be evaluated
> as an lvalue. Since your logic is specific to CallExpr, it really could
> just be sunk into processing of CallExpr itself. When the caller passses
> down 'AsLValue', it is indicating that the parent expects that the child is
> evaluated as an lvalue, but that's based on the parent's context. There is
> no such information here.
Thanks for explaining. It's a revelation to me to learn that:
1. The "AsLValue" bit encodes whether the *consumer* of an expression
uses it as an LValue, not whether the expression itself is an LValue.
2. The caller of Visit*() must provide this bit accurately. It's not
OK for the caller to be "conservative" (i.e. say "AsLValue" when
the consumer of the expression only needs an RValue).
I suspected #1 was the case, but wasn't certain. I wasn't sure about
#2 at all (hence my patch is the way it is). The fact that all tests
passed with the patch led me to think that it's fine for the caller of
Visit*() to say AsLValue when only an RValue is needed. Now I see
that it's just inadequate test coverage.
I'll document this in my new patch. Also, how would you suggest to
add a test to catch violation of #2?
> Further, I think it's no technically correct. Consider:
> const int &foo();
> int i = foo();
> This is what the AST looks like for the second line:
> (DeclStmt 0x101842c18 <line:3:3, col:16>
> 0x101818510 "int i =
> (CallExpr 0x1018185d8 <col:11, col:15> 'const int' lvalue
> (ImplicitCastExpr 0x1018185b8 <col:11> 'const int &(*)(void)'
> <FunctionToPointerDecay>
> (DeclRefExpr 0x101818560 <col:11> 'const int &(void)' lvalue
> FunctionDecl='foo' 0x101818450)))"))
> In this example, CallExpr does indeed return an lvalue. The problem is that
> there is an implicit lvalue-to-rvalue conversion. So the semantics really
> are:
> (DeclStmt 0x101842c18 <line:3:3, col:16>
> 0x101818510 "int i =
> (RValueConversion <--- I made this up
> (CallExpr 0x1018185d8 <col:11, col:15> 'const int' lvalue
> (ImplicitCastExpr 0x1018185b8 <col:11> 'const int &(*)(void)'
> <FunctionToPointerDecay>
> (DeclRefExpr 0x101818560 <col:11> 'const int &(void)' lvalue
> FunctionDecl='foo' 0x101818450)))"))
> Currently we don't represent lvalue-to-rvalue conversions explicitly in the
> AST, although in my conversations with John McCall there is interest in
> doing this. The problem with your patch is that the parent expression may
> actually expect that there is an rvalue, even though the subexpression
> returns an lvalue. Your patch doesn't actually address this problem at all,
> but actually papers over it.
> Codegen goes through some histrionics to infer these semantics from the AST,
> but they analyzer mostly goes off the AST itself for semantics. As you can
> see this is a problem, since we really have no "sequence point" for the
> analyzer to do the lvalue-to-rvalue conversion. We get around this
> limitation in various places when it comes to C-style constructs, but there
> are many corner cases for C++.
This is really unfortunate. :-(
> In this particular case, I think we need to add special support in the CFG
> builder for UnaryOperator. Specifically, we need to add a
> 'VisitUnaryOperator' to the CFGBuilder class, and have it determine, base on
> the opcode, whether or not the operation requires the subexpression to be an
> lvalue or rvalue. It then passes the appropriate value for AddStmtChoice.
I'll try that.
One question: for the purpose of the CFG builder, does an LValue have
to be mutable? For example, given
void Foo(const Bar& x) { ... }
Bar& GetBar() { ... }
when the analyzer sees "Foo(GetBar());", should it treat "GetBar()" as
an LValue or RValue? What if the implementation of Foo() actually
cares about the identity of the input, e.g.
void Foo(const Bar& x) {
if (&x == &someGlobalBarVairalbe)
...;
}
? Thanks.
> Similarly, for CallExprs, any CallExpr that returns a reference needs to be
> treated as returning an lvalue. The problem here is that we don't have a
> sequence point for doing arbitrary lvalue-to-rvalue conversion in the AST,
> but this is something we can possibly model in GRExprEngine given enough
> breadcrumbs in the CFG.
--
Zhanyong
More information about the cfe-dev
mailing list