我是Frama-C的新手用户,并且有一些关于断言的问题 指针。
考虑下面涉及的C片段:
现在,为什么使用WP验证程序(“frama-c -wp file.c”)无法声明A5和A6 由于start_operation()上的最后一个“ensure”子句,它们不应该成立吗?
我做错了什么?
最佳,
爱德华多typedef enum
{
PENDING, NOT_PENDING
} DataState;
typedef struct
{
DataState state;
int value;
} Data;
typedef struct
{
Data* data;
int id;
} Handle;
/*@
@ ensures \valid(\result);
@ ensures \result->state == NOT_PENDING;
@*/
Data* init(void);
/*@
@ requires data->state == NOT_PENDING;
@ ensures data->state == PENDING;
@ ensures \result->data == data;
@*/
Handle* start_operation(Data* data, int to);
/*@
@ requires handle->data->state == PENDING;
@ ensures handle->data->state == NOT_PENDING;
@ ensures handle->data == \old(handle)->data;
@*/
void wait(Handle* handle);
int main(int argc, char** argv)
{
Data* data = init();
/*@ assert A1: data->state == NOT_PENDING; */
Handle* h = start_operation(data,0);
/*@ assert A2: data->state == PENDING; */
/*@ assert A3: h->data == data; */
wait(h);
/*@ assert A4: h->data->state == NOT_PENDING; */
/*@ assert A5: data->state == NOT_PENDING; */
/*@ assert A6: h->data == data; */
return 0;
}
答案 0 :(得分:2)
你正在为“新手”验证一些棘手的记忆操作。
ACSL构造\old
的效果与您认为的完全不同。
首先,后置条件中的\old(handle)
与handle
相同,因为handle
是参数。合同旨在从呼叫者的角度使用。即使函数wait
修改了handle
(这可能是不寻常的但是可能的),它的契约仍然打算将调用者作为参数传递的值与函数返回给调用者的状态相关联。
简而言之,在ACSL后置条件中,parameter
始终意味着\old(parameter)
(即使该函数像本地变量一样修改parameter
)。< / p>
其次,上述规则仅适用于参数。对于全局变量和内存访问,后置条件被认为适用于后置状态,正如您对其名称所期望的那样。您编写的构造\old(handle)->data
,相当于handle->data
,意味着“句柄的旧值指向新状态的字段.data
”,以便后条件handle->data == \old(handle)->data
是一个重言式,可能不是你想要的。
从上下文来看,您似乎打算使用handle->data == \old(handle->data)
,这意味着“.data
的旧值在新状态中指向的字段handle
等于该字段.data
旧值handle
指向旧状态的{{1}}。通过该更改,程序中的所有断言都会得到证实(使用Alt-Ergo 0.93)。