python - 将 C99 代码翻译成 Z3,细微的细节
问题描述
我遇到了一些我不太了解的微妙细节。假设以下示例,我正在开发一个删除死代码的工具:
int main(){
if(1==0){
neverexecutes();
}
}
我将其转换为 AST ( pycparser ),当遇到 if 条件时,1==0
我使用以下方法将其转换为 Z3:
def evaluate_ast(self, node: c_ast.Node):
"""Translates a c_ast.Node to a z3 predicate."""
typ = type(node)
if typ == c_ast.BinaryOp:
leftnode = self.evaluate_ast(node.left)
rightnode = self.evaluate_ast(node.right)
if node.op == '&&':
return And(leftnode, rightnode)
elif node.op == '||':
return Or(leftnode, rightnode)
elif node.op == '==':
return leftnode == rightnode
elif node.op == '<':
return leftnode < rightnode
elif node.op == '<=':
return leftnode <= rightnode
elif node.op == '>':
return leftnode > rightnode
elif node.op == '>=':
return leftnode >= rightnode
elif node.op == '!=':
return leftnode != rightnode
elif node.op == '/':
return leftnode / rightnode
elif node.op == '+':
return leftnode + rightnode
elif typ == c_ast.Assignment and node.op == '=':
leftnode = self.evaluate_ast(node.lvalue)
rightnode = self.evaluate_ast(node.rvalue)
return leftnode == rightnode
(...)
被1==0
翻译成k!0
并且求解器回答sat
,这是不正确的。
如果我像这样改变对 C99 平等的处理:
elif node.op == '==':
return And(leftnode == rightnode)
它有效,我假设我对所有二元运算符都有同样的问题。我在这里想念什么?一个只有False
不应该的系统unsat
?我认为这k!0
只是对 Z3 中某些错误值的翻译。
我认为这也使我的问题更好一些:
>>> from z3 import *
>>> s = Solver()
>>> s.add(False)
>>> s.check()
unsat
>>> s.reset()
>>> s.add(1==0)
>>> s.check()
unsat
>>> s.reset()
>>> s.add(Bool(1==0))
>>> s.check()
sat
False
和1==0
和有什么区别Bool(1==0)
?
解决方案
The problem here is that Bool
takes a name and makes a symbolic value out of it. You need to use BoolVal
instead. In these cases the sexpr
method is your friend for debugging purposes:
>>> from z3 import *
>>> s = Solver()
>>> s.add(1==0)
>>> print(s.sexpr())
(assert false)
The above is OK, because the add
method is smart to treat values correctly. You could've wrapped it around BoolVal
for the same effect:
>>> from z3 import *
>>> s = Solver()
>>> s.add(BoolVal(1==0))
>>> print(s.sexpr())
(assert false)
But see what happens if you wrap it around Bool
:
>>> from z3 import *
>>> s = Solver()
>>> s.add(Bool(1==0))
>>> print(s.sexpr())
(declare-fun k!0 () Bool)
(assert k!0)
And that is the essence of your problem. (Looks like z3 is coming up with an internal name k!0
since 1==0
isn't a valid SMTLib name here. Which adds to the confusion.)
Note that z3py has similar functions for Int
/IntVal
, BitVec
/BitVecVal
, Real
/RealVal
etc. that you have to be careful with similarly.
This is one of the weakest points of z3py unfortunately: Due to Python's untyped nature, different functions try to interpret all sorts of inputs you can provide and they are sometimes inconsistent and it can be very hard to detect and debug such issues. If you suspect something is off printing s.sexpr()
is your best friend. (A similar recent bug was reported here and subsequently fixed, for instance.)
推荐阅读
- sdn - 如何将 Virtualbox 机器作为主机连接到 Mininet OVS 交换机以进行 SDN 实验?
- c# - 错误 CS0029 无法将类型“字符串”隐式转换为“双精度”
- kubernetes - 我可以在 Kubernetes 上使用 Google 管理的证书制作 AMQPS RabbitMQ 吗?
- google-drive-api - 谷歌驱动器阻止访问 Colab 中的文件
- c++ - C ++ - 声明指向返回任何类型并获取任意数量参数的函数的指针
- android - 约束布局和视图旋转不调整视图大小
- python - Miniet 网络安全
- docker - 如何基于最小镜像创建和运行 Docker 容器?
- python - 使用 Python 3.8 写入文件时出现权限错误
- c++ - 如何从另一个库导入 C++ 模块?