Module DomainGCL

Require Import String.
Require Import Impure.
Require Import PredTrans.
Require Export DomainInterfaces.


Module GeneralizedBasicGCL (Export Imp: FullImpureMonad) (N: NumSig) (D: BasicDomainG Imp N).


We inline MP definition, but emulate the constructor and the spec projection of the initial version !
  Record cdac: Type :=
    raw_build_cdac {
      impl: D.t -> imp D.t ;
      sapp: (Mem.t N.t) -> ((Mem.t N.t) -> Prop) -> Prop ;
      sapp_monotone: forall m (P Q: Mem.t N.t -> Prop), sapp m P -> (forall m', P m' -> Q m') -> sapp m Q ;
      impl_correct:
        forall a, WHEN a' <- impl a THEN forall m, D.gamma a m -> sapp m (D.gamma a')
    }.

  Bind Scope cdac_scope with cdac.
  Delimit Scope cdac_scope with cdac.

  Program Definition build_cdac
     (impl: D.t -> imp D.t)
     (spec: (Mem.t N.t) -> MPP (Mem.t N.t) | forall a, WHEN a' <- impl a THEN forall m, D.gamma a m -> spec m (D.gamma a')) : cdac
  :=
    {|
       impl := impl ;
       sapp := fun m => spec m
    |}.
  Obligation 1.
    eapply apply_monotone; eauto.
  Qed.

  Program Definition spec (gc: cdac) m: MPP (Mem.t N.t) :=
    {| apply := sapp gc m |}.
  Obligation 1.
    eapply sapp_monotone; eauto.
  Qed.
  Extraction Inline impl spec build_cdac.
  Hint Resolve sapp_monotone impl_correct apply_monotone: vpl.

  Coercion spec: cdac >-> Funclass.
  Notation "#" := impl: cdac_scope.

  Open Scope cdac.

  Program Definition cast (gc: cdac)
           (spec': (Mem.t N.t) -> MPP (Mem.t N.t) | forall m, (spec' m °|= gc m)%MPP): cdac
     := build_cdac
(#gc)
spec'
        .
  Obligation 1.
     xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline cast.

  Program Definition skip: cdac
     := build_cdac
pure
Skip
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline skip.

  Import String.
  Program Definition fail (msg: string): cdac
     := build_cdac
(fun a => pure (failwith msg a))
Skip
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline fail.

  Program Definition seq (gc1 gc2: cdac) : cdac
     := build_cdac
(fun a => BIND a' <- #gc1 a -;
                              BIND b <- D.isBottom a' -;
                              if b then
                                pure a'
                              else
                                #gc2 a')
(gc1 °; gc2)%MPP
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
    intros; eapply sapp_monotone; eauto with vpl.
    intros m' X; case (H0 m'); auto.
  Qed.   Extraction Inline seq.

  Program Definition join gc1 gc2: cdac
     := build_cdac
(fun a => BIND a1 <- #gc1 a -;
                              BIND a2 <- #gc2 a -;
                              D.join a1 a2)
(gc1 °\/ gc2)%MPP
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
    simpl in * |- *; intuition eauto with vpl.
  Qed.   Extraction Inline join.

  Program Definition loop gc (oracle: D.t -> imp D.t): cdac
   := build_cdac
(fun a =>
                       BIND inv <- oracle a -;
                       BIND b <- D.isIncl a inv -;
                       if b
                       then
                         BIND a' <- #gc inv -;
                         BIND b' <- D.isIncl a' inv -;
                         if b'
                         then pure inv
                         else
                           let inv:=failwith "invariant preservation" D.top in
                           BIND _ <- #gc inv -;
                           pure inv
                       else
                         let inv:=failwith "invariant init" D.top in
                         BIND _ <- #gc inv -;
                         pure inv)
     (UMeet (fun (inv: Mem.t N.t -> Prop) =>
          °|- inv °;
          °|- (fun _ => forall m', inv m' -> gc m' inv) °;
          (UJoin (fun m => Update (fun _ => m) °; °-| inv))))%MPP
       .
  Obligation 1.
     xasimplify ltac:(eauto with vpl).
     - constructor 1 with (x:=D.gamma exta). intuition eauto with vpl.
     - unfold failwith; constructor 1 with (x:=(fun _ => True));
       intuition eauto with vpl.
     - unfold failwith; constructor 1 with (x:=(fun _ => True));
       intuition eauto with vpl.
  Qed.   Extraction Inline loop.

  Definition skip_info:=("#skip#")%string.

  Program Definition try {A:Type} (o: option A) (gc: A -> cdac): cdac :=
     build_cdac
(fun a => match o with
                           | Some x => #(gc x) a
                           | None =>
                                      let a:=trace INFO skip_info a in pure a
                         end)
(Try o (fun x => (gc x)) Skip).
  Obligation 1.
    destruct o; simpl; xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline try.

  Program Definition bind {A:Type} (ge: D.t -> imp A) (gc: A -> cdac)
           (post: A -> Mem.t N.t -> Prop | forall a, WHEN r <- ge a THEN forall m, D.gamma a m -> post r m)
   := build_cdac
(fun a => BIND x <- ge a -;
                            #(gc x) a)
(UMeet (fun x => °|- (post x) °; (gc x)))%MPP
      .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline bind.

  Close Scope cdac.

End GeneralizedBasicGCL.

Module BasicGCL (N: NumSig) (D: BasicDomain N).
  Include GeneralizedBasicGCL Core N D.
End BasicGCL.

Module GeneralizedFullGCL (Export Imp: FullAlarmedMonad) (N: NumSig) (Import Cond: XCondSig N) (D: FullAbstractDomainG Imp N Cond).

  Include GeneralizedBasicGCL Imp N D.

  Program Definition assume cond : cdac
    := build_cdac
(D.assume cond)
(°-| (fun m => Cond.sat cond m))%MPP
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline assume.

  Definition assert_msg: string := "failed assert:"%string.

  Program Definition assert msg cond: cdac
    := build_cdac
(fun a => BIND b <- D.assert cond a -;
                    if b then
                      pure a
                    else
                      Imp.alarm (assert_msg ++ msg) a)
(°|- (Cond.sat cond))%MPP
       .
  Obligation 1.
    xasimplify ltac:(intuition eauto with vpl);
    case (mayReturn_alarm _ _ _ _ Hexta0).
  Qed.   Extraction Inline assert.

  Program Definition assign x te : cdac
    := build_cdac
(D.assign x te)
(Update (fun m => Mem.assign x (Term.eval te m) m))%MPP
       .
  Obligation 1.
    xasimplify ltac:(eauto with vpl).
  Qed.   Extraction Inline assign.

End GeneralizedFullGCL.

Now, add actually alarm handling !

Module CoreAlarm <: FullAlarmedMonad.
  Include AlarmImpureMonad Core.
End CoreAlarm.

Straight-forward lifting of a FullAbstractDomain without alarm into a FullAbstractDomain on CoreAlarm.
Module LiftCoreAlarm (N: NumSig) (Cond: XCondSig N) (D: FullAbstractDomain N Cond) <: FullAbstractDomainG CoreAlarm N Cond.

  Import CoreAlarm.

  Definition t:=D.t.

  Definition gamma:=D.gamma.

  Instance gamma_ext: Proper (Logic.eq ==> pointwise_relation PVar.t Logic.eq ==> iff) gamma.
  Proof D.gamma_ext.

  Hint Resolve Base.mayReturn_lift: vpl.

  Ltac lift_simplify name name_correct :=
    unfold name; intros; unfold wlp;
    intros; eapply name_correct; eauto with vpl.

  Definition isIncl a a' := lift (D.isIncl a a').
  Lemma isIncl_correct: forall a1 a2, If (isIncl a1 a2) THEN forall m, gamma a1 m -> gamma a2 m.
Proof.
    lift_simplify isIncl D.isIncl_correct.
  Qed.

  Definition top:=D.top.
  Lemma top_correct: forall m, gamma top m.
  Proof D.top_correct.

  Definition join (a1 a2: t): imp t := lift (D.join a1 a2).

  Lemma join_correct: forall a1 a2, WHEN p <- join a1 a2 THEN forall m, gamma a1 m \/ gamma a2 m -> gamma p m.
Proof.
    lift_simplify join D.join_correct.
  Qed.

  Definition widen (a1 a2:t): imp t := lift (D.widen a1 a2).

  Definition bottom := D.bottom.
  Lemma bottom_correct: forall m, (gamma bottom m)->False.
  Proof D.bottom_correct.

  Definition isBottom (a:t): imp bool := lift (D.isBottom a).
  Lemma isBottom_correct: forall a, If isBottom a THEN forall m, (gamma a m)->False.
Proof.
    lift_simplify isBottom D.isBottom_correct.
  Qed.

  Definition project (a:t) (x:PVar.t) : imp t := lift (D.project a x).
  Lemma project_correct: forall a x, WHEN p <- project a x THEN forall m v, gamma a m -> gamma p (Mem.assign x v m).
Proof.
    lift_simplify project D.project_correct.
  Qed.

  Definition assume c a:= lift (D.assume c a).
  Lemma assume_correct: forall c a, WHEN p <- assume c a THEN forall m, Cond.sat c m -> gamma a m -> gamma p m.
Proof.
    lift_simplify assume D.assume_correct.
  Qed.
   
  Definition assert c a:= lift (D.assert c a).
  Lemma assert_correct: forall c a, If assert c a THEN forall m, gamma a m -> Cond.sat c m.
Proof.
    lift_simplify assert D.assert_correct.
  Qed.

  Definition assign x t a := lift (D.assign x t a).
  Lemma assign_correct: forall x te a, WHEN p <- assign x te a THEN
    forall m, gamma a m -> gamma p (Mem.assign x (Cond.Term.eval te m) m).
Proof.
    lift_simplify assign D.assign_correct.
  Qed.

  Definition guassign x c a := lift (D.guassign x c a).
  Lemma guassign_correct: forall x c a, WHEN p <- guassign x c a THEN
     forall m v, Cond.xsat c m (Mem.assign x v m) -> gamma a m -> gamma p (Mem.assign x v m).
Proof.
    lift_simplify guassign D.guassign_correct.
  Qed.

End LiftCoreAlarm.

Module FullAlarmGCL (N: NumSig) (Cond: XCondSig N) (D: FullAbstractDomain N Cond).
  Module DAlarm.
    Include LiftCoreAlarm N Cond D.
  End DAlarm.
  Include GeneralizedFullGCL CoreAlarm N Cond DAlarm.
End FullAlarmGCL.