Python 将C99代码翻译成Z3,细微细节

Python 将C99代码翻译成Z3,细微细节,python,z3,Python,Z3,我遇到了一些我不太了解的微妙细节。我正在开发一个工具来删除死代码,假设以下示例: int main(){ if(1==0){ neverexecutes(); } } 我将其转换为AST(),当遇到if条件1==0时,我使用以下方法将其转换为Z3: def evaluate_ast(self, node: c_ast.Node): """Translates a c_ast.Node to a z3 predicat

我遇到了一些我不太了解的微妙细节。我正在开发一个工具来删除死代码,假设以下示例:

int main(){
    if(1==0){
        neverexecutes();
    }
}
我将其转换为AST(),当遇到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
        (...)
它是有效的,我假设我对所有二进制运算符都有相同的问题。我错过了什么?只有
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)
之间的区别是什么?

这里的问题是
Bool
取一个名称并从中提取符号值。您需要改用
BoolVal
。在这些情况下,出于调试目的,
sexpr
方法是您的朋友:

>>从z3导入*
>>>s=解算器()
>>>s.add(1==0)
>>>打印(s.sexpr())
(断言错误)
以上是可以的,因为
add
方法能够正确处理值。您可以将其包裹在
BoolVal
周围,以获得相同的效果:

>>从z3导入*
>>>s=解算器()
>>>s.add(布尔瓦尔(1==0))
>>>打印(s.sexpr())
(断言错误)
但是看看如果你把它包装起来会发生什么:

>>从z3导入*
>>>s=解算器()
>>>s.add(Bool(1==0))
>>>打印(s.sexpr())
(声明funk!0()Bool)
(断言k!0)
这就是你问题的本质。(看起来z3正在使用一个内部名称
k!0
,因为
1==0
在这里不是有效的SMTLib名称。这增加了混淆。)

请注意,z3py对
Int
/
IntVal
BitVec
/
BitVecVal
Real
/
RealVal
等具有类似的功能,您必须谨慎使用


不幸的是,这是z3py的弱点之一:由于Python的非类型化性质,不同的函数试图解释您可以提供的各种输入,它们有时不一致,并且很难检测和调试此类问题。如果你怀疑打印出了什么东西,sexpr()是你最好的朋友。(例如,最近报告了一个类似的bug,随后修复了。)

这很可能是自动强制转换的问题。尝试在调试器中单步执行,以查看这些值中的哪些实际被转换为Z3表达式。我的猜测是
Bool(1==0)
是一个指向对象的Python(非零)指针,Z3API不知道如何处理它,因此它创建了一个符号常量。是的,我就是问这个问题的同一个人:)除了指南之外,还有其他面向Python的建议吗?或者我应该专注于实现吗?这可能是你最好的选择。我也喜欢基本信息,但你现在可能已经远远超出了这个范围!祝你好运。
>>> 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