-
Notifications
You must be signed in to change notification settings - Fork 273
let_exprt can now do multiple bindings #4992
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
Conversation
8931632
to
13c457b
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 13c457b).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/122229305
src/util/std_expr.h
Outdated
where, | ||
where.type()) | ||
{ | ||
} | ||
|
||
symbol_value_sequencet &symbol_value_sequence() |
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.
So someone retrieving this needs to know it's an alternating sequence of symbol, value, symbol, value? Seems better to either have a sequence of 2-tuples called bindings, or else provide accessors for nth_symbol, nth_value etc. Either of these can also enforce that the symbols are symbol_exprt.
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.
I did consider both options.
Problems of the "accessor" option are that a) you can't easily iterate, and b) it kills the USE_LIST option for performance reasons (but we may want to kill that anyway).
The two-tuple option would be somewhat more memory-consuming, but maybe that's not a genuine issue.
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.
Now with the "sequence of 2-tuples" option.
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.
It doesn't strike me as particularly great design to build something rather fragile just to satisfy a potential, yet unproven future use.
src/util/std_expr.h
Outdated
{ | ||
} | ||
|
||
using symbol_value_sequencet = std::vector<exprt>; |
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.
I know this isn't the first time/only place we are doing this, but we really shouldn't: this should be exprt::operandst
, unless we are very intentionally (but still somewhat questionably) using a subtype that we know has the same memory layout (as was the case for binding_exprt
).
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.
I don't see much of an alternative to strengthen typing while still exposing something that can be iterated over.
An option could be to invent a custom range-like setup, which would essentially wrap the underlying iterator. That would eliminate the scary casts at the level of the container.
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.
I'd be happy to look into this, but given that this is a very global problem, I would declare that to be outside of the scope of this PR.
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.
Well, can we at least not make it any worse? I.e., not introduce yet another local definition of what we believe to be the type of exprt::operands()
?
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.
Without that, it's not possible to type the symbols.
src/util/std_expr.h
Outdated
where, | ||
where.type()) | ||
{ | ||
} | ||
|
||
symbol_value_sequencet &symbol_value_sequence() | ||
{ | ||
return (symbol_value_sequencet &)op0().operands(); |
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.
I do find these type casts scary. Right now, this one actually is unnecessary. But if in future there were to be a difference then only runtime crashes would tell us.
13c457b
to
d5163b7
Compare
src/util/std_expr.h
Outdated
{ | ||
public: | ||
let_exprt(symbol_exprt symbol, exprt value, const exprt &where) | ||
: ternary_exprt( | ||
class let_binding_exprt : public binary_exprt |
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.
Another question: why is a let_binding_exprt
not at all related to a binding_exprt
? That seems surprising to me.
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.
This is not trivial: there's only one "where", and not one per binding, whereas the let bindings need an additional "value". Thus, the "is-a" relationship is not easy to establish.
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.
Ack, but then the binding_exprt
may need to be reconsidered if it isn't actually as generic as was hoped? Maybe it should just not be called binding_exprt
and instead its API should be folded into that of a quantifier_exprt
? I would just like to avoid confusion.
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.
I did consider that, but it does fit array_comprehension_exprt
. That would then need to be a quantifier (odd) or have it's own API.
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.
Another idea. An option could be that let_exprt
gets a binding_exprt
as operand!
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.
That would seem nice and consistent with the other uses of binding_exprt
!
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.
Ok, will go for it!
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.
done
cff58cd
to
eba4578
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: eba4578).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/122360496
This enables let_exprt to use binding_exprt.
This enables let_exprt to do multiple binding, which matches what the SMT-LIB standard does. The new let_exprt can be re-written into a lambda binding plus a function application in the standard way.
eba4578
to
71a20e9
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 71a20e9).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/122980484
This matches what the SMT-LIB standard does. It also matches what our own
binding_exprt
can do, i.e.,let_exprt
can be re-written into a lambdabinding plus a function application in the standard way.