(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(* we can remove the need for functional extensionality, at the
   expense of heavier definitions and proofs, with [m |= P] defined as
   [forall m', eqmap m m' -> P m'] instead of [P m] *)

(*+ Syntax *)


Inductive instruction :=
  | set (x e:nat)      (* mem[x] := e *)
  | assert (x:nat)     (* assert mem[x]<>0 *)
  | release (l:nat)    (* unlocking a lock *)
  | acquire (l:nat)    (* locking a lock *)
  | spawn (f:string) (* spawning function f *).(** spawning could be replaced with

  | spawn (p : list instruction)

and then when reaching it in the oracular semantics, the oracle says
how the current map is split [NO! the oracle would say which map
you're given when you do JOIN, but the safety of the thread has to
provide which map to give away], and then the oracle itself is
(interleaving)-split into two.  We could also write

  | par (p1 p2 : list instruction)

and do the same.  We adopt the spawning with an ID instead because
it's closer to the architecture of VST *)


(*+ General results about safety *)

Definition Rel X := X -> X -> Prop.Inductive star {X} (R : Rel X) : Rel X :=
  | star_refl x : star R x x
  | star_cons x y z : R x y -> star R y z -> star R x z.Inductive star {X} (R : Rel X) : Rel X :=
  | star_refl x : star R x x
  | star_cons x y z : R x y -> star R y z -> star R x z.Inductive star {X} (R : Rel X) : Rel X :=
  | star_refl x : star R x x
  | star_cons x y z : R x y -> star R y z -> star R x z.Definition safe {X} (R : Rel X) x := forall y, star R x y -> exists z, R y z.

Lemma safe_det {X} (R : Rel X) x y : safe R y -> R x y -> (forall y', R x y' -> y = y') -> safe R x.