Thanks for your reply!
Right, sure, but I don’t have a convenient way to find that
DecompositionDecl from a given BindingDecl,
and sometimes I need to act based on the BindingDecl alone.
Can you give an example?
Let’s say a structured binding is written into, and then some function is
called, and then we read from that binding again.
If it was global, we have to assume any function call can invalidate it,
but if it’s local and it wasn’t passed as a parameter,
chances are it will remain the same.
I think that will just work if you expand references to BindingDecls into
their binding expressions: both before and after, you'll end up evaluating
an lvalue denoting the same subobject of the DecompositionDecl.
It was chosen because it matches the semantic model desired by the
designers of the feature. For example, you can't handle bitfields if you
model structured bindings as reference variables.
This was a somewhat controversial decision, but it doesn't look like it's
going to be reversed.
That's what the CFG for the above function should represent.
Sorry, I’m not sure what do you mean here: from my understanding, CFG
does not transform AST inside statements (apart from maybe tiny syntactic
IIRC, the CFG expands CXXDefaultArgExprs and CXXDefaultInitExprs; this
case is analogous to those.
Thank you for the explanation! I was also very confused.
We do expand CXXDefaultInitExprs for sema warnings CFG, but not for the
CFG analyzer. We don't expand CXXDefaultArgExprs yet.
I have a concern, which is probably invalid, about expanding expressions
that can appear more than once. So far in a lot of places (in the analyzer
in particular) we've been relying on every expression appearing only once
within a single CFG, which would no longer be the case if we expand
CXXDefaultArgExprs or DeclRefExprs-to-BindingDecls.
In particular, in the analyzer it's common to keep track of "the value of
the expression". We are unable to handle the situation when the same
expression, within the same call stack frame, in the same moment of time,
has two different values. For BindingDecl this shouldn't be a problem
because its expression will always have the same value.
Yes, a BindingDecl should always evaluate to the same lvalue throughout its
lifetime (at least, per the current language rules; there's been some
discussion of changing the semantics so that get<N>(e) is evaluated each
time the binding is named, but so far that looks unlikely to happen).
For CXXDefaultArgExpr it seems to also not be a problem because it is
always surrounded by its respective function call, which is already the
next moment of time - and we won't expand the same CXXDefaultArgExpr twice
in the same call. Even if CXXDefaultArgExpr calls itself recursively, we're
not on the same stack frame anymore.
Interesting, you evaluate default arguments in the callee's stack frame?
(We've historically had problems with this in the constant expressoin
evaluator, where we're now numbering "versions" of variables and
temporaries to distinguish them in situations like this -- see
https://reviews.llvm.org/D42776). It's not completely clear to me that you
can't need to have the same CXXDefaultArgExpr live twice at the same time;
a construct like:
int f(int a = f(0), int b = g());
... would trigger that if it were valid, and it's conceivable that there
are variants of that which are valid.
Am i understanding correctly that we're not ever going to be required to
maintain two different values for CXXDefaultArgExpr sub-expressions or for
binding sub expressions? Is there something that protects us from such
situations, apart from "that's where we are at the moment"?
Good question. I don't think so. And we already can't rely on that for
The other problem i'm immediately seeing in the analyzer is to prevent it
from thinking that we've found an infinite loop when we visit the same
expression twice without any change in the state of the program (which is a
known but presumably relatively harmless bug in our current
CXXDefaultArgExpr handling). But that should be relatively easy to resolve.
There is also a certain amount of "unknown unknowns" here, because i've no
idea in how many other places we rely upon, or will need to rely upon every
expression appearing only once.
The alternative i've been considering for now was to provide "fake stack
frames" for evaluating such expandable expressions instead of expanding
them directly into the CFG. This would be an analyzer-side fix, but it may
be fine if other users of the CFG are fine with expanding.
It might be also possible to deep-copy the expressions when expanding. But
in case of CXXDefaultArgExpr it would assume deep-copying arbitrary
expressions, which sounds hard.
Yes. Another option might be to stop using the Expr* to identify the CFG
element / value, but I suspect that would be a very substantial undertaking?