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!