-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Handle additional cases in rule_properties::check_accessor #5821
Conversation
src/muz/base/rule_properties.cpp
Outdated
// if one branch is not(recognizer) then the accessor is safe | ||
if (m.is_not(arg)) { | ||
if (is_recognizer(to_app(arg)->get_arg(0))) | ||
goto _continue; |
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.
Is the goto
really required? It looks to me that you can just use continue
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 need to skip the todo.push_back
, so just continue
from the inner for is not enough.
I'm not really happy about using goto
, to remove it I could:
- use
std::any_of
andcontinue
But this would loop twice over argsif (m.is_or(parent)) { for (expr* arg : *to_app(parent)) add_recognizer(arg); if (std::any_of(to_app(parent)->begin(), to_app(parent)->end(), [&](expr *arg) { return m.is_not(arg, arg) && is_recognizer(arg); })) continue; }
- use a boolean flag and then continue if the flag is true
- include the "or not" case in
is_recognizer
Probably (1) is the cleaner one, even if it requires an additional loop/lambda.
I noticed that goto
is used more than std::any_of
in the codebase, so let me know which one you prefer for consistency.
src/muz/base/rule_properties.cpp
Outdated
recognizers.push_back(arg); | ||
// if one branch is not(recognizer) then the accessor is safe | ||
if (m.is_not(arg)) { | ||
if (is_recognizer(to_app(arg)->get_arg(0))) |
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 allows formulas like (==> (is_cons x) (= y (tail x))
. I don’t consider this guarded because the second literal could be set to true even when the first is set to false. Please comment on why this is considered guarded.
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 should be fine. It is de-morgan dual to conjunction and context based.
dually, there could be a conjunction (not (and (is_cons x) (not (= y (tail x))))
The context-sensitive analysis determines that the conjunction is true only if (is_cons x)
src/muz/base/rule_properties.cpp
Outdated
if (to_app(parent)->get_arg(1) == e && is_recognizer(to_app(parent)->get_arg(0))) | ||
continue; // e is guarded | ||
else | ||
if (m_dt.is_recognizer(to_app(parent)->get_arg(0))) |
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.
Leaves some room as the ite condition could be conjunctive. So an abstraction of this test could be to extract 'must' recognizers from an expression. Overall, a more satisfactory approach would address complete patterns for nested cases which this code doesn't address.
match (x, y) with
a::as, [] ->
[], b::bs ->
[], [] ->
_, _ -> body with hd(x), tl(y)....
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.
There is another bug: e could be in both branches of the ite.
Then checking that it is in the if-branch and continuing masks that it is in the else branch.
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.
Leaves some room as the ite condition could be conjunctive
If I understand correctly, the smtlib match syntax disallows nested patterns Sec 3.6.1, Remark 4 of SMT-LIB 2.6 Standard. So such a pattern matching expression would not be possible unless it is desugared "externally" into ite
s with conjunctive conditions.
In our WebSpec project, we compile nested patterns into nested smtlib match expressions, so we handle this case by first desugaring it into standard smtlib expressions and then run Z3.
Does Z3 support nested patterns by creating a match expression using another frontend/API?
Thanks for your comments! I've modified the loop to do a full depth first search over all parents, keeping track of the size of the I'm using an |
This PR adds additional safe accessor patterns to the previously accepted ones.
Instead of only allowing guarded accessors, this patch allows for an accessor to be used unguarded if all other recognizers are used in the other paths (so the only possible value at that point of the expression is the last remaining constructor). These new accepted patterns should cover all possible ways a match expression is desugared:
and
: if all branches except for one have negated recognizers, then the remaining one is safeor
: if all branches except for one have recognizers the remaining one is safeor
: if one branch has a negated recognizer (for the current case) access in another branch is safeite
: if the recognizer is in the condition, access in "then" branch is safe; the "else" branch is safe if all recognizers except for one have been used in the condition of anite
in the path to the current accessorThis PR improves the fix of #4869 by allowing pattern matching expressions where the last branch accesses the constructor arguments. The previous version of
check_accessor
incorrectly returned false for the example of #4869 if the cases of the match expression are swapped:In that example, the match is desugared as:
where
(valueInt (:var 0))
is a safe (but unguarded) accessor because it is in the else branch and it is the only remaining constructor. The previous check would not allow this accessor because it is unguarded.(Edit: clarified second case in the above list)