像在逻辑中一样,准确性的对偶是完备性。在第12.3节中,我们将程序 P 的分析的soundness定义为性质 $\alpha(\{[P]\}) \sqsubseteq [[P]]$。因此,自然而然地,我们可以定义分析对于 P 是complete的,如果满足以下性质:
$$ [\![P]\!] \sqsubseteq \alpha(\{\![P]\!\}) $$
一个分析是complete,那么就是对于所有的程序都是complete。如果一个分析同时满足sound和complete,那么就是 $[\![P]\!] = \alpha(\{\![P]\!\})$。(也就是说,具体执行的结果进行抽象,和抽象的结果一致)
在第12.4节中,我们研究了抽象的最优性概念,这是由于希望定义分析约束尽可能精确,相对于所选择的分析格。类似地,我们可以问,对于程序 P,使用当前的分析格,分析结果 [[P]] 是否尽可能精确?更正式地说,问题是$[\\![P]\\!] = \\alpha(\\{\\![P]\\!\\})$是否成立;换句话说,这个性质与分析对于 P 是准确且完备的相一致。
很多时候,即使我们得到抽象域上求解的最佳抽象的定义(比如符号分析的eval函数),分析也不会是对所有程序都满足sound和complete,例如如下例子:
x = input; // x = T
y = x; // x = T, y = T
z = x - y; // x = T, y = T, z = 0(sound 那么就需要是T)
令 $\sigma$ 表示语句y=x之后的抽象状态,那么我们有:$\sigma(x) = \sigma(y) = \top$。 对于单个语句 z = x - y
语义的任何准确的抽象都将导致一个将 z
映射到 T 的抽象状态,但答案 0
将在最终程序点的分析结果中更为精确且仍然准确。直观地说,分析不知道 x
和 y
之间的关联。
我们来想想为什么x和y之间的关联无法获得? 因为我们用的是基于lattice理论的dataflow分析技术。在Sign这个lattice里面,我们是没有维护等价关系的!那么为什么我们不设计一个更好的lattice呢?其实可以,我们可以再引入一个lattice表示两个变量是否相等。那么程序的格就是两个子抽象域的组合。那么分析的复杂度就更高了!
对于这个特定的示例程序,原则上我们可以通过改变约束生成规则来提高分析的精度,以识别由语句 y = x 后跟 z = x - y 组成的特殊模式。与采用这样的临时方法以获得精度不同,通常更可行的解决方案是关系分析(参见第7章的relational analysis)。
在12.3节中,我们知道,soundness的定义有两种 $\\alpha(\\{\\![P]\\!\\}) \\sqsubseteq [\\![P]\\!]$ 或者 $\\{\\![P]\\!\\} \\sqsubseteq \\gamma([\\![P]\\!])$。但是对于completeness属性,我们只能用 $[\\![P]\\!] \\sqsubseteq \\alpha(\\{\\![P]\\!\\})$。
这里面为什么只能在抽象里面分析?因为在抽象里面,大一定大!但是反过来抽象的值,经过具体化会非常多!这些多的内容并不一定 小于或者等于 具体域。比如x=5,y=0,z=x+y。我们可以知道[[P]]是+,{[P]}是{5}。那么对于抽象比较的soundeness:{5}的抽象化是+,所以 $\alpha(\{\![P]\!\}) \sqsubseteq [\![P]\!]$ 满足;+的具体化是{1,2,3,4,…},所以 $\{\![P]\!\} \sqsubseteq \gamma([\![P]\!])$满足。但是对于completeness,{5}的抽象化是+,所以 $[\![P]\!] \sqsubseteq \alpha(\{\![P]\!\})$满足,但是在具体化上,+的具体化是{1,2,3,4,…} 并不是 {5}的子集,那么 $\gamma([\![P]\!]) \sqsubseteq \{\![P]\!\}$ 并不满足。
因此, $\{\![P]\!\} = \gamma([\![P]\!])$是比 $[\![P]\!] = \alpha(\{\![P]\!\})$要严格多的条件。也就是说 $\{\![P]\!\} = \gamma([\![P]\!])$成立,那么分析捕获了程序P的执行语义,并且没有多余的近似,也就是说分析对于程序P是严格精确的(exact)。因为针对于所有程序,一个非平凡的分析一定会有因为抽象而带来的信息损失,因此任何有意义的分析都不能满足exact属性。
这里我们延伸一些,在缺陷检测里面,sound表示没有漏报!为什么呢,就看sound的概念,是说在抽象里面,分析的结果一定比真实情况大,所以这就是没有漏报!而complete就是说没有误报,其含义就是这个分析的结果与真实执行还小,所以就是报出来的都是真实的bug的子集合,所以就是没有误报!
在建立了分析完备性的概念(以及一个较不重要的分析精确性概念)之后,我们通过定义在分析中使用的个别抽象的完备性概念,来了解不准确性可能出现的地方。同之前的章节一致,我们做如下定义:假设L1和L1‘是收集语义的格,L2和L2’是用于分析的格。令 $\\alpha: L_1 \\rightarrow L_2$ 和 $\\alpha': L_1 \\rightarrow L_2'$表示抽象函数, $\\gamma: L_2 \\rightarrow L_1$ 和 $\\gamma': L_2' \\rightarrow L_1'$ 表示具体函数,并且这四个函数形成两个Galois连接。考虑两个单调函数$cg: L_1 \\rightarrow L_1'$ 和 $ag: L_2 \\rightarrow L_2'$。我们说ag函数是cg的一个complete的抽象,如果下述条件满足:
$$ ag ~\circ~\alpha \sqsubseteq \alpha'~\circ~cg $$
让我们看符号分析,在12.4节,我们知道如下乘法的定义是最优解:
事实上,这个定义也满足complete:(简单说就是在具体域里面,分开依次相乘已经枚举了所有的情况。而且相乘不会出现新的情况,比如+-相乘就是-,但是相加则要范围变大)
这个结果可以告诉我们,在乘法运算上,不会丢失任何的精度。