------------------------------------------------------------------------
-- The compiler and VM are sound with respect to the high-level
-- semantics
------------------------------------------------------------------------

module Semantics.SmallStep.CompilerCorrectness.Soundness where

open import Syntax
open import Semantics.SmallStep
open import Semantics.SmallStep.EvalCtxt
import CompilerCorrectness as CC; open CC SmallStep
open import VirtualMachine
open import Infinite hiding (return)

open import Relation.Nullary
open import Category.Monad
open RawMonad ¬¬-Monad hiding (_>>_)
open import Relation.Unary hiding (U)
open import Data.List
open import Data.Product
open import Data.Star hiding (return)
open import Data.Nat as Nat hiding (fold)
open import Relation.Binary
open import Data.Nat.Properties
open import Data.Empty
open import Data.Function

------------------------------------------------------------------------
-- The second part of soundness

module Part2 where

  -- This proof uses the technique advocated by Hu and Hutton: It is
  -- proven that ⟨ comp x ops , i , s ⟩ ⟶∞ implies that either
  --
  --   1) x ˢ ⟶∞[ i ], or
  --
  --   2) x ˢ ⟶⋆[ i ] done v, where ⟨ comp ⌜ v ⌝ ops , i , s ⟩ ⟶∞
  --
  -- (almost). It then follows easily that ⟨ comp x [] , i , [] ⟩ ⟶∞
  -- implies that x ˢ ⟶∞[ i ].
  --
  -- Note that this shows that Hu and Hutton's technique works also
  -- for infinite transition sequences.

  data Cont {t} i ops s : Exprˢ t  Set where
    nat   :  {n} ( :  ops , i , nat n  s  ⟶∞) 
            Cont i ops s (done (nat n))
    throw : ( :  i , s  ⟶∞)  Cont i ops s (done throw)

  Cont[][]=∅ :  {t i}  Empty (Cont {t} i [] [])
  Cont[][]=∅ .(done (nat n)) (nat {n} (() ◅∞ ))
  Cont[][]=∅ .(done throw)   (throw   (() ◅∞ ))

  infix 2 _⟶⋆∞[_]′_

  _⟶⋆∞[_]′_ :  {t}  Exprˢ t  Status  Pred (Exprˢ t)  Set1
  x ⟶⋆∞[ i ]′ P = MaybeInfinite″ (Step i) x P

  sound′ :  {t} (x : Expr t) {i ops s} 
            comp x ops , i , s  ⟶∞  x ˢ ⟶⋆∞[ i ]′ Cont i ops s
  sound′  nat n  (push      ◅∞ ) = _  run  nat n  ◅⟨ Red Val 
                                      _  done (nat n)  ∈⟨ nat  
  sound′  nat n  (interrupt ◅∞ ) = _  run  nat n  ◅⟨ Red (Int _) 
                                      _  done throw    ∈⟨ throw  

  sound′  throw  (throw     ◅∞ ) = _  run  throw  ◅⟨ Red Val 
                                      _  done  throw   ∈⟨ throw  
  sound′  throw  (interrupt ◅∞ ) = _  run  throw  ◅⟨ Red (Int _) 
                                      _  done  throw   ∈⟨ throw  

  sound′ (loop x)  (interrupt ◅∞ ) =
    _  run  (loop (x ˢ)) ◅⟨ Red (Int _) 
    _  done throw        ∈⟨ throw  
  sound′ (loop x)  (loop ◅∞ ) =
    _              run (loop (x ˢ))  ◅⟨ Red Loop 
        run (x ˢ >> run (loop (x ˢ)))
          ∶⟨ map-append⋆∞′  y  run (y >> run (loop (x ˢ)))) Seqnˡ
                           helper (sound′ x ) 
    where
    helper :  {y i ops s} 
             Cont i (pop  loop x  ops) s y 
             run (y >> loop x ˢ) ⟶⋆∞[ i ]′ Cont i ops s
    helper (nat {n} (pop ◅∞ )) ~
      _  run (done (nat n) >> run (loop (x ˢ))) ◅⟨ Red Seqn₁ 
          run (loop (x ˢ))                       ∶⟨ sound′ (loop x)  
    helper (nat {n} (interrupt ◅∞ nat ◅∞ )) =
      _  run  (done (nat n) >> run (loop (x ˢ))) ◅⟨ Red Seqn₁ 
      _  run  (loop (x ˢ))                       ◅⟨ Red (Int _) 
      _  done throw                              ∈⟨ throw  
    helper (throw ) =
      _  run  (done throw >> run (loop (x ˢ))) ◅⟨ Red Seqn₂ 
      _  done throw                            ∈⟨ throw  

  sound′ (x  y)  =
    run (x ˢ  y ˢ) ∶⟨ map-append⋆∞′  x  run (x  y ˢ)) Addˡ
                                     helper₁ (sound′ x ) 
    where
    helper₂ :  {t} {y : Exprˢ t} {i ops s} m 
              Cont i (add  ops) (nat m  s) y 
              run (done (nat m)  y) ⟶⋆∞[ i ]′ Cont i ops s
    helper₂ m (nat {n} (add ◅∞ )) =
      _  run  (done (nat m)  done (nat n)) ◅⟨ Red Add₁ 
      _  done (nat (m + n))                 ∈⟨ nat  
    helper₂ m (nat {n} (interrupt ◅∞ nat ◅∞ nat ◅∞ )) =
      _  run  (done (nat m)  done (nat n)) ◅⟨ Red (Int _) 
      _  done throw                         ∈⟨ throw  
    helper₂ m (throw (nat ◅∞ )) =
      _  run  (done (nat m)  done throw) ◅⟨ Red Add₃ 
      _  done throw                       ∈⟨ throw  

    helper₁ :  {x : Exprˢ _} {i ops s} 
              Cont i (comp y (add  ops)) s x 
              run (x  y ˢ) ⟶⋆∞[ i ]′ Cont i ops s
    helper₁ (nat {m} ) =
      run (done (nat m)  y ˢ)
        ∶⟨ map-append⋆∞′  y  run (done (nat m)  y)) Addʳ
                         (helper₂ m) (sound′ y ) 
    helper₁ (throw ) = _  run (done throw  y ˢ) ◅⟨ Red Add₂ 
                        _  done throw             ∈⟨ throw  

  sound′ (x >> y)  =
    run (x ˢ >> y ˢ) ∶⟨ map-append⋆∞′  x  run (x >> y ˢ)) Seqnˡ
                                      helper (sound′ x ) 
    where
    helper :  {x : Exprˢ _} {i ops s} 
             Cont i (pop  comp y ops) s x 
             run (x >> y ˢ) ⟶⋆∞[ i ]′ Cont i ops s
    helper (nat {n} (pop ◅∞ )) =
      _  run (done (nat n) >> y ˢ) ◅⟨ Red Seqn₁ 
          y ˢ                       ∶⟨ sound′ y  
    helper (nat {n} (interrupt ◅∞ nat ◅∞ )) =
      _  run (done (nat n) >> y ˢ) ◅⟨ Red Seqn₁ 
      _  y ˢ                       ◅⟨ Red (Int (y ˢ-interruptible)) 
      _  done throw                ∈⟨ throw  
    helper (throw ) = _  run  (done throw >> y ˢ) ◅⟨ Red Seqn₂ 
                       _  done throw               ∈⟨ throw  

  sound′ (x catch y) (interrupt ◅∞ ) =
    _  run  (x ˢ catch y ˢ) ◅⟨ Red (Int (x ˢ-interruptible)) 
    _  done throw           ∈⟨ throw  
  sound′ (x catch y) (mark ◅∞ ) =
    run (x ˢ catch y ˢ) ∶⟨ map-append⋆∞′  x  run (x catch y ˢ))
                                         Catchˡ helper₁ (sound′ x ) 
    where
    helper₂ :  {t} {y : Exprˢ t} {i ops s} 
              Cont B (reset  ops) (int i  s) y 
              run (done throw catch y) ⟶⋆∞[ i ]′ Cont i ops s
    helper₂ (nat {n} (reset ◅∞ )) =
      _  run  (done throw catch done (nat n)) ◅⟨ Red Catch₂ 
      _  done (nat n)                         ∈⟨ nat  
    helper₂ (throw (int ◅∞ )) =
      _  run  (done throw catch done throw) ◅⟨ Red Catch₂ 
      _  done throw                         ∈⟨ throw  

    helper₁
      :  {x : Exprˢ _} {i ops s} 
        Cont i (unmark  ops) (han (comp y (reset  ops))  s) x 
        run (x catch y ˢ) ⟶⋆∞[ i ]′ Cont i ops s
    helper₁ (nat {n} (unmark ◅∞ )) =
      _  run  (done (nat n) catch y ˢ) ◅⟨ Red Catch₁ 
      _  done (nat n)                  ∈⟨ nat  
    helper₁ (nat {n} (interrupt ◅∞ nat ◅∞ han ◅∞ )) =
      _  run (done (nat n) catch y ˢ) ◅⟨ Catchˡ (Red (Int _)) 
          run (done throw catch y ˢ)
            ∶⟨ map-append⋆∞′  y  run (done throw catch y)) Catchʳ
                             helper₂ (sound′ y ) 
    helper₁ (throw (han ◅∞ )) =
      run (done throw catch y ˢ)
        ∶⟨ map-append⋆∞′  y  run (done throw catch y)) Catchʳ
                         helper₂ (sound′ y ) 

  sound′ (block x) (interrupt ◅∞ ) =
    _  run  (block (x ˢ)) ◅⟨ Red (Int (x ˢ-interruptible)) 
    _  done throw         ∈⟨ throw  
  sound′ (block x) (set ◅∞ ) =
    run (block (x ˢ)) ∶⟨ map-append⋆∞′  x  run (block x)) Blockˡ
                                       helper (sound′ x ) 
    where
    helper :  {t} {x : Exprˢ t} {i ops s} 
             Cont B (reset  ops) (int i  s) x 
             run (block x) ⟶⋆∞[ i ]′ Cont i ops s
    helper (nat {n} (reset ◅∞ )) =
      _  run  (block (done (nat n))) ◅⟨ Red Block 
      _  done (nat n)                ∈⟨ nat  
    helper (throw (int ◅∞ )) =
      _  run  (block (done throw)) ◅⟨ Red Block 
      _  done throw                ∈⟨ throw  

  sound′ (unblock x) (interrupt ◅∞ ) =
    _  run  (unblock (x ˢ)) ◅⟨ Red (Int (x ˢ-interruptible)) 
    _  done throw           ∈⟨ throw  
  sound′ (unblock x) (set ◅∞ ) =
    run (unblock (x ˢ)) ∶⟨ map-append⋆∞′  x  run (unblock x))
                                         Unblockˡ helper (sound′ x ) 
    where
    helper :  {t} {x : Exprˢ t} {i ops s} 
             Cont U (reset  ops) (int i  s) x 
             run (unblock x) ⟶⋆∞[ i ]′ Cont i ops s
    helper (nat {n} (reset ◅∞ )) =
      _  run  (unblock (done (nat n))) ◅⟨ Red Unblock 
      _  done (nat n)                  ∈⟨ nat  
    helper (nat {n} (interrupt ◅∞ nat ◅∞ int ◅∞ )) =
      _  run  (unblock (done (nat n))) ◅⟨ Unblockˡ (Red (Int _)) 
      _  run  (unblock (done throw))   ◅⟨ Red Unblock 
      _  done throw                    ∈⟨ throw  
    helper (throw (int ◅∞ )) =
      _  run  (unblock (done throw)) ◅⟨ Red Unblock 
      _  done throw                  ∈⟨ throw  

  sound :  {t} (x : Expr t) {i}  Sound₂ x i
  sound x x⟶∞ = return (⋆∞⇒∞′ (⋆∞″→⋆∞ (sound′ x x⟶∞)) Cont[][]=∅)

------------------------------------------------------------------------
-- The first part of soundness

module Part1 where

  -- This proof is quite ugly, but it may be easier to come up with
  -- than the bar theorem in
  -- Semantics.BigStep.CompilerCorrectness.BarTheorem.

  -- ℓ ≥ s₁ ⟶⋆ s₂ is a transition sequence from s₁ to s₂ of length at
  -- most ℓ. The proof below is by induction over ℓ.

  _≥_⟶⋆_ :   State  State  Set
    s₁ ⟶⋆ s₂ = Σ (s₁ ⟶⋆ s₂) λ seq  len seq  
    where len = fold  _ _  ) (const suc) 0

  data Sound′  {t} (x : Expr t) i ops s c : Set where
    nat   :  {n} (x⟶⋆ : x ˢ ⟶⋆[ i ] done (nat n))
            ( :    ops , i , nat n  s  ⟶⋆ c) 
            Sound′  x i ops s c
    throw : (x⟶⋆ : x ˢ ⟶⋆[ i ] done throw)
            ( :    i , s  ⟶⋆ c)  Sound′  x i ops s c

  inc :  { t} {x : Expr t} {i ops s c} k 
        Sound′  x i ops s c  Sound′ (k + ) x i ops s c
  inc k (nat   x⟶⋆ ( , le)) = nat   x⟶⋆ ( , ≤-steps k le)
  inc k (throw x⟶⋆ ( , le)) = throw x⟶⋆ ( , ≤-steps k le)

  comp-not-final :  {t} (x : Expr t) {ops i s} 
                    λ st   comp x ops , i , s   st
  comp-not-final  nat n    = (, push)
  comp-not-final  throw    = (, throw)
  comp-not-final (loop x)    = (, loop)
  comp-not-final (x  y)     = comp-not-final x
  comp-not-final (x >> y)    = comp-not-final x
  comp-not-final (x catch y) = (, mark)
  comp-not-final (block x)   = (, set)
  comp-not-final (unblock x) = (, set)

  catch-lemma :  { t} {x y : Expr t} {i ops s c} 
                x ˢ ⟶⋆[ i ] done throw 
                Sound′ (suc ) y B (reset  ops) (int i  s) c  c  
                Sound′  (x catch y) i ops s c
  catch-lemma x⟶⋆ (nat   y⟶⋆ (ε , _)) c↛ = ⊥-elim (c↛ (, reset))
  catch-lemma x⟶⋆ (throw y⟶⋆ (ε , _)) c↛ = ⊥-elim (c↛ (, int))
  catch-lemma x⟶⋆ (nat   y⟶⋆ (reset   , s≤s le)) _ = nat   (lift⟶⋆ ( •catch _) x⟶⋆ ◅◅
                                                              lift⟶⋆ (handler• ) y⟶⋆ ◅◅ Red Catch₂  ε)
                                                             ( , le)
  catch-lemma x⟶⋆ (throw y⟶⋆ (int     , s≤s le)) _ = throw (lift⟶⋆ ( •catch _) x⟶⋆ ◅◅
                                                              lift⟶⋆ (handler• ) y⟶⋆ ◅◅ Red Catch₂  ε)
                                                             ( , le)

  sound′ :  { t} (x : Expr t) {i ops s c} 
           suc    comp x ops , i , s  ⟶⋆ c  c   Sound′  x i ops s c
  sound′ x         (ε      , _)  c↛ = ⊥-elim (c↛ (comp-not-final x))
  sound′  nat n  (push       ⟶⋆c , s≤s le) c↛ = nat   (Red Val      ε) (⟶⋆c , le)
  sound′  nat n  (interrupt  ⟶⋆c , s≤s le) c↛ = throw (Red (Int _)  ε) (⟶⋆c , le)
  sound′  throw  (throw      ⟶⋆c , s≤s le) c↛ = throw (Red Val      ε) (⟶⋆c , le)
  sound′  throw  (interrupt  ⟶⋆c , s≤s le) c↛ = throw (Red (Int _)  ε) (⟶⋆c , le)

  sound′ (loop x) (interrupt  ⟶⋆c , s≤s le) c↛ = throw (Red (Int _)  ε) (⟶⋆c , le)
  sound′ (loop x) (loop       ⟶⋆c , s≤s le) c↛ with sound′ x (⟶⋆c , ≤-step le) c↛
  ... | nat   x⟶⋆v (pop   , s≤s le′)  = inc 1 (sound′ (loop x) ( , ≤-step le′) c↛)
  ... | nat   x⟶⋆v (interrupt  nat  
                   , s≤s (s≤s le′))     = inc 2 (throw (Red (Int _)  ε) ( , le′))
  ... | throw x⟶⋆v                     = throw (Red Loop 
                                                 lift⟶⋆ ( •>> _) x⟶⋆v ◅◅
                                                 Red Seqn₂  ε) 
  ... | nat   x⟶⋆v (ε             , _) = ⊥-elim (c↛ (, pop))
  ... | nat   x⟶⋆v (interrupt  ε , _) = ⊥-elim (c↛ (, nat))

  sound′ (x  y) ⟶⋆c c↛ with sound′ x ⟶⋆c c↛
  sound′ (x  y) ⟶⋆c c↛ | throw x⟶⋆v          = throw (lift⟶⋆ ( •⊕ _) x⟶⋆v ◅◅ Red Add₂  ε) 
  sound′ (x  y) ⟶⋆c c↛ | nat   x⟶⋆v ( , le′) with sound′ y ( , ≤-step le′) c↛
  ...   | nat   y⟶⋆v (add  …′ , s≤s le″)        = inc 1 (nat (lift⟶⋆ ( •⊕ _)     x⟶⋆v ◅◅
                                                               lift⟶⋆ (nat _ ⊕• ) y⟶⋆v ◅◅
                                                               Red Add₁  ε)
                                                              (…′ , le″))
  ...   | nat   y⟶⋆v (interrupt  nat  nat  …′
                     , s≤s (s≤s (s≤s le″)))      = inc 3 (throw (Red (Int (x ˢ-interruptible))  ε)
                                                                (…′ , le″))
  ...   | throw y⟶⋆v (nat  …′ , s≤s le″)        = inc 1 (throw (lift⟶⋆ ( •⊕ _)     x⟶⋆v ◅◅
                                                                 lift⟶⋆ (nat _ ⊕• ) y⟶⋆v ◅◅
                                                                 Red Add₃  ε)
                                                                (…′ , le″))
  ...   | nat   y⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, add))
  ...   | nat   y⟶⋆v (interrupt  ε       , _) = ⊥-elim (c↛ (, nat))
  ...   | nat   y⟶⋆v (interrupt  nat  ε , _) = ⊥-elim (c↛ (, nat))
  ...   | throw y⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, nat))

  sound′ (x >> y) ⟶⋆c c↛ with sound′ x ⟶⋆c c↛
  ... | nat   x⟶⋆v (ε                   , _)       = ⊥-elim (c↛ (, pop))
  ... | nat   x⟶⋆v (interrupt  ε       , s≤s le′) = ⊥-elim (c↛ (, nat))
  ... | nat   x⟶⋆v (interrupt  nat   , s≤s (s≤s le′)) = inc 2 (throw (Red (Int (x ˢ-interruptible))  ε) ( , le′))
  ... | throw x⟶⋆v                                      = throw (lift⟶⋆ ( •>> _) x⟶⋆v ◅◅ Red Seqn₂  ε) 
  ... | nat   x⟶⋆v (pop               , s≤s le′) with sound′ y ( , ≤-step le′) c↛
  ...   | nat   y⟶⋆v …′ = inc 1 (nat   (lift⟶⋆ ( •>> _) x⟶⋆v ◅◅ Red Seqn₁  y⟶⋆v) …′)
  ...   | throw y⟶⋆v …′ = inc 1 (throw (lift⟶⋆ ( •>> _) x⟶⋆v ◅◅ Red Seqn₁  y⟶⋆v) …′)

  sound′ (x catch y) (interrupt  ⟶⋆c , s≤s le) c↛ =
    throw (Red (Int (x ˢ-interruptible))  ε) (⟶⋆c , le)
  sound′ (x catch y) (mark  ⟶⋆c , s≤s le) c↛ with sound′ x (⟶⋆c , ≤-step le) c↛
  ... | nat   x⟶⋆v (unmark   , s≤s le′)     = inc 1 (nat (lift⟶⋆ ( •catch y ˢ) x⟶⋆v ◅◅ Red Catch₁  ε) ( , le′))
  ... | nat   x⟶⋆v (interrupt  nat  han  
                   , s≤s (s≤s (s≤s le′)))     = inc 1 (catch-lemma (Red (Int (x ˢ-interruptible))  ε)
                                                                        (sound′ y ( , ≤-steps 4 le′) c↛) c↛)
  ... | throw x⟶⋆v (han   , s≤s le′)        = inc 1 (catch-lemma x⟶⋆v (sound′ y ( , ≤-steps 2 le′) c↛) c↛)
  ... | nat   x⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, unmark))
  ... | nat   x⟶⋆v (interrupt  ε       , _) = ⊥-elim (c↛ (, nat))
  ... | nat   x⟶⋆v (interrupt  nat  ε , _) = ⊥-elim (c↛ (, han))
  ... | throw x⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, han))

  sound′ (block x) (interrupt  ⟶⋆c , s≤s le) c↛ =
    throw (Red (Int (x ˢ-interruptible))  ε) (⟶⋆c , le)
  sound′ (block x) (set  ⟶⋆c , s≤s le) c↛ with sound′ x (⟶⋆c , ≤-step le) c↛
  ... | nat   x⟶⋆v (reset   , s≤s le′) = inc 1 (nat   (lift⟶⋆ (block• ) x⟶⋆v ◅◅ Red Block  ε) ( , le′))
  ... | throw x⟶⋆v (int     , s≤s le′) = inc 1 (throw (lift⟶⋆ (block• ) x⟶⋆v ◅◅ Red Block  ε) ( , le′))
  ... | nat   x⟶⋆v (ε , _) = ⊥-elim (c↛ (, reset))
  ... | throw x⟶⋆v (ε , _) = ⊥-elim (c↛ (, int))

  sound′ (unblock x) (interrupt  ⟶⋆c , s≤s le) c↛ =
    throw (Red (Int (x ˢ-interruptible))  ε) (⟶⋆c , le)
  sound′ (unblock x) (set  ⟶⋆c , s≤s le) c↛ with sound′ x (⟶⋆c , ≤-step le) c↛
  ... | nat   x⟶⋆v (reset   , s≤s le′)      = inc 1 (nat   (lift⟶⋆ (unblock• ) x⟶⋆v ◅◅ Red Unblock  ε) ( , le′))
  ... | nat   x⟶⋆v (interrupt  nat  int  
                   , s≤s (s≤s (s≤s le′)))     = inc 3 (throw (lift⟶⋆ (unblock• ) x⟶⋆v ◅◅
                                                              Unblockˡ (Red (Int _))  Red Unblock  ε) ( , le′))
  ... | throw x⟶⋆v (int   , s≤s le′)        = inc 1 (throw (lift⟶⋆ (unblock• ) x⟶⋆v ◅◅ Red Unblock  ε) ( , le′))
  ... | nat   x⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, reset))
  ... | nat   x⟶⋆v (interrupt  ε       , _) = ⊥-elim (c↛ (, nat))
  ... | nat   x⟶⋆v (interrupt  nat  ε , _) = ⊥-elim (c↛ (, int))
  ... | throw x⟶⋆v (ε                   , _) = ⊥-elim (c↛ (, int))

  sound :  {t} (x : Expr t) {i}  Sound₁ x i
  sound x (term x⟶c c↛)
    with sound′ x (x⟶c , ≤-step (Poset.refl Nat.poset)) c↛
  ... | nat   x⟶⋆v (ε      , _) = returns x⟶⋆v
  ... | nat   x⟶⋆v (()  _ , _)
  ... | throw x⟶⋆v (ε      , _) = throws x⟶⋆v
  ... | throw x⟶⋆v (()  _ , _)

------------------------------------------------------------------------
-- Both parts together

sound :  {t} (e : Expr t) {i}  Sound e i
sound e = ((λ {_}  Part1.sound e) , Part2.sound e)