Set Implicit Arguments.
Require Import MyTactics.
Require Import Arithmetic.
Require Import DeBruijn.
Require Import Layers.
Require Import Coercions.
Require Import Programs.
Require Import ValueLists.
Require Import RawPrograms.
Require Import Erasure.
 ------------------------------------------------------------------------- 
 The functions reveal_value and reveal_term indicate how values and
   terms are transformed by the revelation of an extra capability. 
 If v has type T, then reveal_value v has type TyTensor T U. In
   particular, if v is a function, then it takes and returns an extra
   argument, a capability. 
 If t has type T, then reveal_term k t has type TyTensor T U, under
   an environment where a binding of k to U has been inserted -- this
   represents the extra capability that is revealed. We do not impose k = 0,
   because this added flexibility simplifies the transformation of
   TLetUnpack and other binding constructs. 
 In the definition of reveal_term, every call to reveal_value is
   followed with a use of lift_value k, so as to compensate for the
   insertion of the new capability at index k. 
Fixpoint reveal_value (
v : 
value) :=
  
match v with
  | 
VVar _
  | 
VUnit _ =>
      
v
  | 
VAbs t =>
 The function argument x is decomposed into a pair y, z, where
         y is the original argument and z is the new capability. 
         References to x within t are (transparently) replaced with
         references to y -- this is index 1 -- and an application of
         lift_term 2 is used in order to jump above the now unused
         binding of x. 
      VAbs (
TLetPair Physical Logical (
VVar 0) (
lift_term 2 (
reveal_term 0 
t)))
  | 
VPair layer1 layer2 v1 v2 =>
      
VPair layer1 layer2 (
reveal_value v1) (
reveal_value v2)
  | 
VTyAbs v =>
      
VTyAbs (
reveal_value v)
  | 
VPack v =>
      
VPack (
reveal_value v)
  | 
VCoApp c v =>
 At coercion applications, the revelation lemma for subtyping is used. 
      VCoApp (
co_tensor c) (
reveal_value v)
  | 
VBang v =>
      
VBang (
reveal_value v)
  | 
VLoc l v =>
      
VLoc l (
reveal_value v)
  | 
VRegionCap vs =>
      
VRegionCap (
reveal_values vs)
  | 
VRegionCapPunched vs =>
      
VRegionCapPunched (
reveal_values vs)
  | 
VInhabitant v =>
      
VInhabitant (
reveal_value v)
  
end
with reveal_values (
vs : 
values) :=
  
match vs with
  | 
VNil =>
      
VNil
  | 
VCons v vs =>
      
VCons (
reveal_value v) (
reveal_values vs)
  
end
with reveal_term (
k : 
nat) (
t : 
term) :=
  
match t with
  | 
TVal v =>
 At values, we must return a pair of the original result --
         the value -- and the extra capability -- which has index k. 
      TVal (
VMixPair (
lift_value k (
reveal_value v)) (
VVar k))
  | 
TApp v t =>
 At function applications, the argument -- a term -- produces a pair,
         and the function expects a pair, so there is nothing to do. 
      TApp (
lift_value k (
reveal_value v)) (
reveal_term k t)
  | 
TMixPair t v =>
 At mixed pairs, the extra pair that the tensor creates must commute
         with the pair that was there in the first place. 
      TCoApp CoMixPairExchange (
TMixPair (
reveal_term k t) (
lift_value k (
reveal_value v)))
  | 
TTyAbs t =>
 At type abstractions, the natural translation produces a universally
         quantified pair, whose right-hand component we must extract out of the
         universal quantification -- or, conversely, we must intrude the universal
         quantifier into the pair. 
      TCoApp (
CoForallPairLeft Physical Logical) (
TTyAbs (
reveal_term k t))
  | 
TCoApp c t =>
 At coercion applications, the revelation lemma for subtyping is used,
         but only in the left-hand side of the value/capability pair. 
      TCoApp (
CoMixPair (
co_tensor c) 
CoId) (
reveal_term k t)
  | 
TLetUnpack v t =>
 The easiest way of translating a binding construct is to let the new
         variable have index 0, as usual -- so the extra capability, which
         was at index k, now is at index k+1. 
      TLetUnpack (
lift_value k (
reveal_value v)) (
reveal_term (
S k) 
t)
  | 
TLetBang v t =>
      
TLetBang (
lift_value k (
reveal_value v)) (
reveal_term (
S k) 
t)
  | 
TLetPair layer1 layer2 v t =>
      
TLetPair layer1 layer2 (
lift_value k (
reveal_value v)) (
reveal_term (
S (
S k)) 
t)
  | 
TPrimApp p v =>
 At a primitive operation, the extra capability must be framed out
         while the primitive operation is carried out. 
      TMixPair
        (
TPrimApp p (
lift_value k (
reveal_value v)))
        (
VVar k)
  | 
THide v t =>
 Let us write reveal v for reveal_value v and reveal k in t for
         reveal_term k t. Let us further use informal nominal syntax. We
         must now offer a definition for
           reveal y in (hide x = v in t)
         The definition that we propose is:
           hide z = (reveal v, y) in
           let (x, y) = z in
           let x = CoTensorExchange 0 x in
           CoComposExchange (reveal y in t)
         where the replacement of x with CoTensorExchange 0 x is in fact
         performed via a substitution, not via a let construct, so as to
         avoid disturbing the erasure of the term.
         The term reveal y in t has two free variables, namely x and y,
         and we wish to place it in the scope of a hide construct, which
         supports only one free variable, so we must (somewhat artifically,
         but that is not a problem) pair them up before entering the hide
         construct and deconstruct the pair immediately after entering it.
         What is more subtle is that the type of x has two tensors -- one
         created by reveal v, the other created by hide -- and they are
         in the wrong order, so we must use CoTensorExchange 0 to swap them.
         Symmetrically, the result produced by reveal y in t also has two
         tensors, again in the wrong order, so we must use CoComposExchange
         to swap them. 
      THide
        (
VMixPair (
lift_value k (
reveal_value v)) (
VVar k))
        (
lift_term (
S k) (
          
TLetMixPair (
VVar 0) (
lift_term 2 (
            
subst_term (
VCoApp (
CoTensorExchange 0) (
VVar 1)) 1 (
lift_term 2 (
              
TCoApp CoComposExchange (
reveal_term 0 
t)
        ))))))
  
end.
Fixpoint fold_reveal_value (
nh : 
nat) 
v :=
  
match nh with
  | 0 =>
      
v
  | 
S nh =>
      
fold_reveal_value nh (
reveal_value v)
  
end.
 ------------------------------------------------------------------------- 
 Up to erasure, revelation has no effect. 
Hint Extern 1 => 
rewrite <- 
raw_lift_subst_2_term : 
erase_reveal.
Hint Extern 1 => 
rewrite <- 
raw_lift_subst_2_term_rawverased : 
erase_reveal.
Hint Extern 1 => 
rewrite raw_lift_lift_value : 
erase_reveal.
Lemma erase_reveal:
(
  
forall v,
  
erase_value (
reveal_value v) = 
erase_value v
) /\ (
  
forall vs : 
values,
  
pwe vs (
reveal_values vs)
) /\ (
  
forall t k,
  
erase_term (
reveal_term k t) = 
lift_raw_term k (
erase_term t)
).
Proof.
Lemma erase_reveal_value: 
  
forall v,
  
erase_value (
reveal_value v) = 
erase_value v.
Proof.
Lemma erase_reveal_term: 
  
forall t k,
  
erase_term (
reveal_term k t) = 
lift_raw_term k (
erase_term t).
Proof.
Hint Resolve (
pi2 erase_reveal).
Lemma fold_erase_reveal:
  
forall nh v,
  
erase_value (
fold_reveal_value nh v) = 
erase_value v.
Proof.
 ------------------------------------------------------------------------- 
 Revelation commutes with lifting. This means, in particular, that applying
   revelation to a closed value yields a closed value. 
Lemma lift_reveal:
(
  
forall v j,
  
lift_value j (
reveal_value v) = 
reveal_value (
lift_value j v)
) /\ (
  
forall vs : 
values,
  
True
) /\ (
  
forall t k j,
  
k <= 
j ->
  
lift_term (
S j) (
reveal_term k t) = 
reveal_term k (
lift_term j t)
).
Local Hint Extern 1 (
_ = 
_) => 
congruence.
Proof.
Lemma well_scoped_reveal:
  
forall v k,
  
well_scoped_lift lift_value k v ->
  
well_scoped_lift lift_value k (
reveal_value v).
Proof.
  unfold well_scoped_lift. 
intros. 
rewrite (
pi1 lift_reveal). 
f_equal. 
eauto.
Qed.
 
Lemma closed_reveal:
  
forall v,
  
closed_value v ->
  
closed_value (
reveal_value v).
Proof.
 ------------------------------------------------------------------------- 
 A coercion that lifts a bunch of composition operators out of a pair. 
 This is pretty boring. One could probably avoid the need for a recursive
   definition if one exploited the fact that active hide constructs extrude
   only one at a time (so n is always 0 or 1). 
 This coercion is used in the definition of the semantics, and we do not
   want the semantics to depend on the typing rules, so we must separate
   the definition of the coercion and the proof of its derived typing rule. 
Fixpoint CoMixPairCompossExchange (
n : 
nat) (
t : 
term) : 
term :=
  
match n with
  | 0 =>
      
t
  | 
S n =>
      
TCoApp (
co_composs n CoMixPairComposExchange) (
CoMixPairCompossExchange n t)
  
end.
Lemma erase_CoMixPairCompossExchange:
  
forall n t,
  
erase_term (
CoMixPairCompossExchange n t) = 
erase_term t.
Proof.
  induction n; simpl; eauto.
Qed.