I have the following simple model:
-- SIGNATURES
sig Device {}
sig Account {
var devices: set Device,
}
-- CONSTRAINTS
// Initial conditions
fact init {
no devices // No devices, they will get added using addDevice
}
// Always true
fact {
// If a device is in Account.devices, it got there through addDevice
always (all a: Account, d: Device | (d in a.devices) implies once addDevice[a, d])
}
-- PREDICATES
pred addDevice[a: Account, d: Device] {
(a -> d) not in devices // guard
devices' = devices + (a -> d) // next state devices adds association
}
fun addDevice : Account -> Device {
{ a: Account, d: Device | addDevice[a, d] }
}
-- CHECK
check {
no devices // No devices in the init state
eventually some a: Account, d: Device |
((a -> d) in devices') implies (once addDevice[a, d])
}
The second fact
is intended to enforce that the only way a Device can get added to devices
is if addDevice
was called on it.
The check
statement is intended to check the assertion above: that if we start with an empty devices
, and eventually there is an association Account -> Device
in devices, it implies that addDevice was once called. However, when I run it, it gives me counterexamples where devices
is always empty.
I think what Alloy is trying to tell me is that the only way for this assertion to not be true is if devices
is always empty? But doesn’t that violate the eventually some a: Account, d: Device | ((a -> d) in devices')
part?
Either the second fact
is expressed incorrectly or my check statement is not expressing what I intended.
Please help clarify my misunderstanding, thank you!