Skip to content

state machine refinement structure #2

@tjhance

Description

@tjhance

Currently our refinement structure looks like: we have a bunch of state machines, A, B, C, ... etc. with refinement theorems of the form A_Refines_B.i.dfy. These are all named pretty consistently (although something much less consistent is the state machine invariants; sometimes the invariants for A are in the A file itself, and sometimes they are in separate files).

Sometimes, the A_Refines_B.i.dfy files are interesting, and sometimes they are not. There are a lot of files A_Refines_C which simply compose two existing refinements, A_Refines_B and B_Refines_C. All these 'composition' refinements have nearly the exact same boiler plate. With a "module system" on the horizon, we can start to think about writing a generic refinement composition proof. See this proof-of-concept.

However, there's another issue that needs to be taken care of first, which has to do with the way the refinement theorems are stated.

Here's a typical refinement theorem statement (Betree_Refines_Map.i.dfy):

  lemma RefinesNext(s: Betree.Variables, s':Betree.Variables, uiop: UI.Op)
    requires Inv(s)
    requires Betree.Next(s, s', uiop)
    ensures Inv(s')
    ensures MS.Next(I(s), I(s'), uiop)
  {
    // ...
  }

Note that this has Inv(s) as a pre-condition. Often, the interpretation function in these refinements has Inv as a precondition as well. The consequence of this is that A_Refines_B only composes with B_Refines_C if A.Inv implies B.Inv. This is problematic for a bunch of reasons, including:

  • It makes it impossible to write a generic composition proof within the module system (no such proof would be allowed to rely on an SMT condition like A.Inv ==> B.Inv)
  • Creates long dependency chains where invariants include other invariants that they don't really need.(*) This is bad for proof isolation.

(*) One (the only?) exception is ByteBlockCacheSystem, which includes the invariants of BlockCacheSystem for nontrivial reasons.

The solution to this is to define our generic refinement theorem without reference to 'Inv'. For example, we could use 'Reachable' instead (i.e., Reachable(s) := exists s0...sn . Init(s) && (forall i . Next(s_i, s_(i+1))) && sn == s). Reachable is the universal invariant which implies Inv for any predicate Inv satisfying the usual inductiveness conditions. We could also use an even more general definition of refinement, like behavior-based refinement. (Actually, this might be desirable for other reasons, as it would let us freely and locally introduce 'history' into state machines, which has come up in a few hypotheticals.) Either way, we need to pick some generic definition of refinement which actually composes properly, but which is implied by our usual TLA-style refinement statement. (Of course, we would define generic module-theorems that transform one style into another, so we'd keep writing proofs as before.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions