mirror of
https://github.com/CatalaLang/catala.git
synced 2024-11-08 17:10:22 +03:00
644 lines
19 KiB
Plaintext
644 lines
19 KiB
Plaintext
module Catala.DefaultCalculus
|
|
|
|
(*** Syntax *)
|
|
|
|
type ty =
|
|
| TBool : ty
|
|
| TUnit : ty
|
|
| TArrow : tin:ty -> tout:ty -> ty
|
|
|
|
type var = int
|
|
|
|
type lit =
|
|
| LEmptyError : lit
|
|
| LConflictError : lit
|
|
| LTrue : lit
|
|
| LFalse : lit
|
|
| LUnit : lit
|
|
|
|
type exp =
|
|
| EVar : v:var -> exp
|
|
| EApp : fn:exp -> arg:exp -> exp
|
|
| EAbs : v:var -> vty:ty -> body:exp -> exp
|
|
| ELit : l:lit -> exp
|
|
| EIf : test:exp -> btrue:exp -> bfalse:exp -> exp
|
|
| EDefault: just:exp -> cons:exp -> subdefaults:list exp -> exp
|
|
|
|
(*** Operational semantics *)
|
|
|
|
(**** Helpers *)
|
|
|
|
let c_err = ELit LConflictError
|
|
|
|
let e_err = ELit LEmptyError
|
|
|
|
val is_value : exp -> Tot bool
|
|
let is_value e =
|
|
match e with
|
|
| EAbs _ _ _
|
|
| ELit _
|
|
| EDefault (EAbs _ _ _) _ _
|
|
-> true
|
|
| _ -> false
|
|
|
|
let rec map (#a: Type) (#b: Type) (l:list a) (f: ((x:a{x << l}) -> Tot b)) : Tot (list b)
|
|
=
|
|
match l with
|
|
| [] -> []
|
|
| a::tl -> f a::map tl f
|
|
|
|
val subst : var -> exp -> e:exp -> Tot exp (decreases e)
|
|
let rec subst x e e' =
|
|
match e' with
|
|
| EVar x' -> if x = x' then e else e'
|
|
| EAbs x' t e1 ->
|
|
EAbs x' t (if x = x' then e1 else (subst x e e1))
|
|
| EApp e1 e2 -> EApp (subst x e e1) (subst x e e2)
|
|
| ELit l -> ELit l
|
|
| EIf e1 e2 e3 -> EIf (subst x e e1) (subst x e e2) (subst x e e3)
|
|
| EDefault just cond subs -> EDefault (subst x e just) (subst x e cond) (map subs (subst x e))
|
|
|
|
type empty_count_result =
|
|
| AllEmpty
|
|
| OneNonEmpty of exp
|
|
| Conflict
|
|
|
|
let rec empty_count (acc: empty_count_result) (l: list exp) : Tot empty_count_result (decreases l) =
|
|
match l with
|
|
| [] -> acc
|
|
| hd::tl -> begin
|
|
match (hd, acc) with
|
|
| ELit (LEmptyError), AllEmpty -> empty_count AllEmpty tl
|
|
| ELit (LEmptyError), OneNonEmpty e -> empty_count (OneNonEmpty e) tl
|
|
| _, Conflict -> Conflict
|
|
| _, AllEmpty -> empty_count (OneNonEmpty hd) tl
|
|
| _ -> Conflict
|
|
end
|
|
|
|
(**** Stepping judgment *)
|
|
|
|
let rec step_app
|
|
(e: exp)
|
|
(e1: exp{e1 << e})
|
|
(e2: exp{e2 << e})
|
|
: Tot (option exp) (decreases %[e; 0]) =
|
|
if is_value e1 then
|
|
match e1 with
|
|
| ELit LConflictError -> Some c_err
|
|
| ELit LEmptyError -> Some e_err
|
|
| _ ->
|
|
if is_value e2 then
|
|
match e1 with
|
|
| EAbs x t e' -> Some (subst x e2 e')
|
|
| EDefault (EAbs xjust tjust ejust') econs subs -> (* beta_d *)
|
|
Some (EDefault
|
|
(subst xjust e2 ejust')
|
|
(EApp econs e2)
|
|
(map subs (fun sub -> EApp sub e2)))
|
|
| _ -> None
|
|
else
|
|
match step e2 with
|
|
| Some (ELit LConflictError) -> Some (ELit LConflictError)
|
|
| Some (ELit LEmptyError) -> Some (ELit LEmptyError)
|
|
| Some e2' -> Some (EApp e1 e2')
|
|
| None -> None
|
|
else
|
|
match step e1 with
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some (ELit LEmptyError) -> Some e_err
|
|
| Some e1' -> Some (EApp e1' e2)
|
|
| None -> None
|
|
|
|
and step_if
|
|
(e: exp)
|
|
(e1: exp{e1 << e})
|
|
(e2: exp{e2 << e})
|
|
(e3: exp{e3 << e})
|
|
: Tot (option exp) (decreases %[e; 1]) =
|
|
if is_value e1 then
|
|
match e1 with
|
|
| ELit LConflictError -> Some c_err
|
|
| ELit LEmptyError -> Some e_err
|
|
| ELit LTrue -> Some e2
|
|
| ELit LFalse -> Some e3
|
|
| _ -> None
|
|
else
|
|
match (step e1) with
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some (ELit LEmptyError) -> Some e_err
|
|
| Some e1' -> Some (EIf e1' e2 e3)
|
|
| None -> None
|
|
|
|
and step_subdefaults_left_to_right
|
|
(e: exp)
|
|
(just:exp{just << e})
|
|
(cons:exp{cons << e})
|
|
(subs: list exp{subs << e})
|
|
: Tot (option exp) (decreases %[e; 2; subs])
|
|
=
|
|
match subs with
|
|
| [] -> Some (EDefault just cons [])
|
|
| hd::tl ->
|
|
if is_value hd then
|
|
match step_subdefaults_left_to_right e just cons tl with
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some (EDefault just cons tl') -> Some (EDefault just cons (hd::tl'))
|
|
| _ -> None
|
|
else
|
|
match step hd with
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some hd' -> Some (EDefault just cons (hd'::tl))
|
|
| _ -> None
|
|
|
|
and step_subdefaults_just_false
|
|
(e: exp)
|
|
(just:exp{just << e})
|
|
(cons:exp{cons << e})
|
|
(subs: list exp{subs << e}) : Tot (option exp) (decreases %[e; 3]) =
|
|
if List.Tot.for_all (fun sub -> is_value sub) subs then
|
|
match empty_count AllEmpty subs with
|
|
| AllEmpty -> Some (ELit LEmptyError) (* DefaultJustifFalseNoSub *)
|
|
| OneNonEmpty e' -> Some e' (* DefaultJustifFalseOneSub *)
|
|
| Conflict -> Some (ELit LConflictError) (* DefaultJustifFalseSubConflict *)
|
|
else
|
|
match step_subdefaults_left_to_right e just cons subs with
|
|
| Some e' -> Some e'
|
|
| _ -> None
|
|
|
|
and step_default
|
|
(e: exp)
|
|
(just:exp{just << e})
|
|
(cons:exp{cons << e})
|
|
(subs: list exp{subs << e}) : Tot (option exp) (decreases %[e; 4]) =
|
|
if is_value just then begin
|
|
match just with
|
|
| ELit LConflictError -> Some c_err
|
|
| ELit LEmptyError -> Some e_err
|
|
| ELit _ | EAbs _ _ _ | EDefault (EAbs _ _ _) _ _ -> begin
|
|
match just, cons with
|
|
| EAbs _ _ _, EAbs _ _ _
|
|
| EDefault (EAbs _ _ _) _ _, EDefault (EAbs _ _ _) _ _ ->
|
|
None
|
|
| ELit LTrue, ELit LEmptyError ->
|
|
Some (EDefault (ELit LFalse) cons subs)
|
|
(* DefaultJustifTrueError *)
|
|
| ELit LTrue, _ (* DefaultJustifTrueNoError *) ->
|
|
if is_value cons then
|
|
Some cons
|
|
else begin
|
|
match (step cons) with
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some cons' -> Some (EDefault just cons' subs)
|
|
| None -> None
|
|
end
|
|
| ELit LFalse, _ ->
|
|
step_subdefaults_just_false e just cons subs
|
|
(* here we evaluate the subs from left to right *)
|
|
| _ -> None
|
|
end
|
|
end
|
|
else
|
|
match (step just) with
|
|
| Some just' -> Some (EDefault just' cons subs)
|
|
| Some (ELit LConflictError) -> Some c_err
|
|
| Some (ELit LEmptyError) -> Some e_err
|
|
| None -> None
|
|
|
|
and step (e: exp) : Tot (option exp) (decreases %[e; 5]) =
|
|
match e with
|
|
| EApp e1 e2 -> step_app e e1 e2
|
|
| EIf e1 e2 e3 -> step_if e e1 e2 e3
|
|
| EDefault just cons subs -> step_default e just cons subs
|
|
| _ -> None
|
|
|
|
(* Testing *)
|
|
let _ =
|
|
let e0 = EApp (EAbs 0 TBool (EIf (EVar 0) (ELit LFalse) (ELit LTrue))) (ELit LTrue) in
|
|
let e1 = EIf (ELit LTrue) (ELit LFalse) (ELit LTrue) in
|
|
let e1' = step e0 in
|
|
assert_norm(e1' == Some e1);
|
|
let e2 = ELit LFalse in
|
|
let e2' = step e1 in
|
|
assert_norm(e2' == Some e2)
|
|
|
|
(* Testing *)
|
|
let _ =
|
|
let e0 = EDefault
|
|
(EAbs 0 TBool (EIf (EVar 0) (ELit LTrue) (ELit LFalse)))
|
|
(EAbs 1 TBool (ELit LTrue))
|
|
[ (EAbs 2 TBool (ELit LEmptyError)); (EAbs 3 TBool (ELit LFalse)) ] in
|
|
assert_norm (step e0 == None);
|
|
let e0 = EApp e0 (ELit LFalse) in
|
|
let e1 = EDefault
|
|
(EIf (ELit LFalse) (ELit LTrue) (ELit LFalse))
|
|
(EApp (EAbs 1 TBool (ELit LTrue)) (ELit LFalse))
|
|
[ (EApp (EAbs 2 TBool (ELit LEmptyError)) (ELit LFalse));
|
|
(EApp (EAbs 3 TBool (ELit LFalse)) (ELit LFalse)) ]
|
|
in
|
|
let e1' = step e0 in (* beta_d *)
|
|
assert_norm(e1' == Some e1);
|
|
let e2 = EDefault
|
|
(ELit LFalse)
|
|
(EApp (EAbs 1 TBool (ELit LTrue)) (ELit LFalse))
|
|
[ (EApp (EAbs 2 TBool (ELit LEmptyError)) (ELit LFalse));
|
|
(EApp (EAbs 3 TBool (ELit LFalse)) (ELit LFalse)) ]
|
|
in
|
|
let e2' = step e1 in (* IfFalse *)
|
|
assert_norm(e2' == Some e2);
|
|
let e3 = EDefault
|
|
(ELit LFalse)
|
|
(EApp (EAbs 1 TBool (ELit LTrue)) (ELit LFalse))
|
|
[ (ELit LEmptyError);
|
|
(EApp (EAbs 3 TBool (ELit LFalse)) (ELit LFalse)) ]
|
|
in
|
|
let e3' = step e2 in (* App *)
|
|
assert_norm(e3' == Some e3);
|
|
let e4 = EDefault
|
|
(ELit LFalse)
|
|
(EApp (EAbs 1 TBool (ELit LTrue)) (ELit LFalse))
|
|
[ (ELit LEmptyError);
|
|
(ELit LFalse) ]
|
|
in
|
|
let e4' = step e3 in (* App *)
|
|
assert_norm(e4' == Some e4);
|
|
let e5 = ELit LFalse in
|
|
let e5' = step e4 in
|
|
assert_norm(e5' == Some e5); (* DefaultJustifFalseOneSub *)
|
|
()
|
|
|
|
(*** Typing *)
|
|
|
|
type env = var -> Tot (option ty)
|
|
|
|
val empty : env
|
|
let empty = fun _ -> None
|
|
|
|
val extend : env -> var -> ty -> Tot env
|
|
let extend g x t = fun x' -> if x = x' then Some t else g x'
|
|
|
|
let rec for_all_defaults (subs: list exp) (f: (sub:exp{sub << subs}) -> bool) : bool =
|
|
match subs with
|
|
| [] -> true
|
|
| hd::tl ->
|
|
if f hd then for_all_defaults tl f else false
|
|
|
|
type tyres =
|
|
| TRBool : tyres
|
|
| TRUnit : tyres
|
|
| TRArrow : tin:ty -> tout:tyres -> tyres
|
|
| TRAny: tyres
|
|
|
|
let rec ty_to_res (t: ty) : tyres = match t with
|
|
| TBool -> TRBool
|
|
| TUnit -> TRUnit
|
|
| TArrow tin tout -> TRArrow tin (ty_to_res tout)
|
|
|
|
let rec unify (t1 t2: tyres) : option tyres =
|
|
match t1, t2 with
|
|
| TRBool, TRBool
|
|
| TRUnit, TRUnit -> Some t1
|
|
| TRAny, TRAny -> Some TRAny
|
|
| TRAny, t2 -> Some t2
|
|
| t1, TRAny -> Some t1
|
|
| TRArrow t11 t12, TRArrow t21 t22 ->
|
|
if t11 = t21 then
|
|
match unify t12 t22 with
|
|
| None -> None
|
|
| Some t -> Some (TRArrow t11 t)
|
|
else None
|
|
| _ -> None
|
|
|
|
let rec unify_comm (t1 t2: tyres) : Lemma (unify t1 t2 == unify t2 t1) =
|
|
match t1, t2 with
|
|
| TRArrow t11 t12, TRArrow t21 t22 ->
|
|
unify_comm t12 t22
|
|
| _ -> ()
|
|
|
|
let rec unify_list (g: env) (subs: list exp) : Tot (option tyres) (decreases %[subs]) =
|
|
match subs with
|
|
| [] -> Some TRAny
|
|
| hd::tl -> begin
|
|
let unif_tl = unify_list g tl in
|
|
match unif_tl with
|
|
| None -> None
|
|
| Some unif_tl -> begin
|
|
match typing g hd with
|
|
| None -> None
|
|
| Some thd ->
|
|
unify thd unif_tl
|
|
end
|
|
end
|
|
|
|
and typing (g: env) (e: exp) : Tot (option tyres) (decreases %[e; 1]) =
|
|
match e with
|
|
| EVar x -> begin
|
|
match g x with
|
|
| None -> None
|
|
| Some t -> Some (ty_to_res t)
|
|
end
|
|
| EAbs x t e1 -> begin
|
|
match typing (extend g x t) e1 with
|
|
| Some t' -> Some (TRArrow t t')
|
|
| None -> None
|
|
end
|
|
| EApp e1 e2 -> begin
|
|
match typing g e1, typing g e2 with
|
|
| Some TRAny, Some t2 ->
|
|
Some TRAny
|
|
| Some (TRArrow t11 t12), Some t2 -> unify (ty_to_res t11) t2
|
|
| _, _ -> None
|
|
end
|
|
| ELit LTrue -> Some TRBool
|
|
| ELit LFalse -> Some TRBool
|
|
| EIf e1 e2 e3 -> begin
|
|
match typing g e1, typing g e2, typing g e3 with
|
|
| Some TRBool, Some t2, Some t3
|
|
| Some TRAny, Some t2, Some t3 -> unify t2 t3
|
|
| _, _, _ -> None
|
|
end
|
|
| EDefault (EAbs xjust tjust ejust) (EAbs xcons tcons econs) subs -> begin (* DefaultFun *)
|
|
if tjust = tcons then
|
|
match typing (extend g xjust tjust) ejust, typing (extend g xcons tcons) econs with
|
|
| Some tjust', Some tcons' -> begin
|
|
match unify tjust' tcons' with
|
|
| Some tjust' -> begin
|
|
match unify_list g subs with
|
|
| None -> None
|
|
| Some unif_subs -> unify (TRArrow tjust tjust') unif_subs
|
|
end
|
|
| None -> None
|
|
end
|
|
| _,_ -> None
|
|
else None
|
|
end
|
|
| EDefault tjust tcons subs -> begin (* DefaultBase *)
|
|
match typing g tjust, typing g tcons with
|
|
| Some TRBool, Some tcons
|
|
| Some TRAny, Some tcons -> begin
|
|
match unify_list g subs with
|
|
| None -> None
|
|
| Some unif_subs -> unify tcons unif_subs
|
|
end
|
|
| _, _ -> None
|
|
end
|
|
| _ -> None
|
|
|
|
(*** Progress *)
|
|
|
|
(**** Progress lemmas *)
|
|
|
|
let is_bool_value_cannot_be_default_abs (g: env) (e: exp) : Lemma
|
|
(requires (is_value e /\ (match typing g e with
|
|
| Some TRAny | Some TRBool -> True
|
|
| _ -> False
|
|
))) (ensures (
|
|
match e with
|
|
| ELit LUnit -> False
|
|
| ELit _ -> True
|
|
| _ -> False
|
|
))
|
|
=
|
|
match e with
|
|
| ELit _ -> ()
|
|
| EDefault (EAbs xjust tjust ejust) (EAbs xcons tcons econs) subs -> begin
|
|
match typing g e with
|
|
| Some TRAny
|
|
| Some TRBool ->
|
|
if tjust = tcons then begin
|
|
match
|
|
typing (extend g xjust tjust) ejust,
|
|
typing (extend g xcons tcons) econs
|
|
with
|
|
| Some tjust', Some tcons' -> begin
|
|
match unify tjust' tcons' with
|
|
| Some tjust' ->
|
|
let te = unify_list g subs in
|
|
assert(typing g e == te)
|
|
| None -> ()
|
|
end
|
|
| _ -> ()
|
|
end else ()
|
|
| _ -> ()
|
|
end
|
|
| _ -> ()
|
|
|
|
let rec unify_compose_ok (x y z: tyres) : Lemma
|
|
(requires (Some? (unify y z) /\ Some? (unify x (Some?.v (unify y z)))))
|
|
(ensures (Some? (unify x z)))
|
|
=
|
|
match x, y, z with
|
|
| TRArrow x1 x2, TRArrow y1 y2, TRArrow z1 z2 -> unify_compose_ok x2 y2 z2
|
|
| _ -> ()
|
|
|
|
#push-options "--fuel 3 --ifuel 2 --z3rlimit 20"
|
|
let typing_conserved_by_list_reduction
|
|
(g: env)
|
|
(just cons: exp)
|
|
(subs: list exp)
|
|
: Lemma
|
|
(requires (
|
|
~ (EAbs? just /\ EAbs? cons) /\
|
|
Some? (typing g (EDefault just cons subs))
|
|
))
|
|
(ensures (Cons? subs ==> Some? (typing g (EDefault just cons (Cons?.tl subs)))))
|
|
=
|
|
match subs with
|
|
| [] -> ()
|
|
| hd1::tl ->
|
|
match typing g just, typing g cons with
|
|
| Some TRBool, Some tcons
|
|
| Some TRAny, Some tcons ->
|
|
let e = EDefault just cons subs in
|
|
let t_out_sub = unify_list g subs in
|
|
let thd1 = Some?.v (typing g hd1) in
|
|
unify_compose_ok tcons thd1 (Some?.v (unify_list g tl))
|
|
| _ -> ()
|
|
#pop-options
|
|
|
|
(**** Progress theorem *)
|
|
|
|
#push-options "--fuel 2 --ifuel 1 --z3rlimit 20"
|
|
val progress : e:exp -> Lemma
|
|
(requires (Some? (typing empty e)))
|
|
(ensures (is_value e \/ (Some? (step e))))
|
|
(decreases %[e; 3])
|
|
let rec progress e =
|
|
match e with
|
|
| EApp e1 e2 ->
|
|
progress e1; begin match typing empty e1 with
|
|
| Some TRAny -> if is_value e1 then
|
|
match e1 with
|
|
| ELit (LEmptyError) | ELit (LConflictError) -> ()
|
|
| EDefault just cons subs -> if is_value e2 then () else progress e2
|
|
else ()
|
|
| _ -> progress e2
|
|
end
|
|
| EIf e1 e2 e3 -> progress e1; progress e2; progress e3;
|
|
if is_value e1 then is_bool_value_cannot_be_default_abs empty e1 else ()
|
|
| EDefault just cons subs ->
|
|
if is_value e then () else progress_defaults e just cons subs
|
|
| _ -> ()
|
|
and progress_defaults
|
|
(e: exp)
|
|
(just: exp{just << e})
|
|
(cons: exp{cons << e})
|
|
(subs: list exp{subs << e}) : Lemma
|
|
(requires (~ (is_value e) /\ e == EDefault just cons subs /\ Some? (typing empty e)))
|
|
(ensures (Some? (step_default e just cons subs)))
|
|
(decreases %[e; 2])
|
|
=
|
|
progress just;
|
|
if is_value just then begin
|
|
is_bool_value_cannot_be_default_abs empty just;
|
|
match just, cons with
|
|
| ELit LTrue, ELit LEmptyError -> ()
|
|
| ELit LTrue, _ -> progress cons
|
|
| ELit LFalse, _ -> progress_defaults_just_false e just cons subs
|
|
end else ()
|
|
and progress_defaults_just_false
|
|
(e: exp)
|
|
(just: exp{just << e})
|
|
(cons: exp{cons << e})
|
|
(subs: list exp{subs << e}) : Lemma
|
|
(requires (
|
|
~ (is_value e) /\ just == ELit LFalse /\
|
|
e == EDefault (ELit LFalse) cons subs /\ Some? (typing empty e)
|
|
))
|
|
(ensures (Some? (step_subdefaults_just_false e just cons subs)))
|
|
(decreases %[e; 1])
|
|
=
|
|
if List.Tot.for_all (fun sub -> is_value sub) subs then () else
|
|
progress_defaults_left_to_right e just cons subs
|
|
and progress_defaults_left_to_right
|
|
(e: exp)
|
|
(just: exp{just << e})
|
|
(cons: exp{cons << e})
|
|
(subs: list exp{subs << e}) : Lemma
|
|
(requires (
|
|
~ (is_value e) /\ just == ELit LFalse /\
|
|
Some? (typing empty (EDefault just cons subs))
|
|
))
|
|
(ensures (Some? (step_subdefaults_left_to_right e just cons subs)))
|
|
(decreases %[e; 0; subs])
|
|
=
|
|
match subs with
|
|
| [] -> ()
|
|
| hd::tl ->
|
|
progress hd;
|
|
if is_value hd then begin
|
|
assert(Some?.v (typing empty just) == TRBool);
|
|
let tcons = Some?.v (typing empty cons) in
|
|
typing_conserved_by_list_reduction empty just cons subs;
|
|
progress_defaults_left_to_right e just cons tl
|
|
end else ()
|
|
#pop-options
|
|
|
|
val appears_free_in : x:int -> e:exp -> Tot bool
|
|
let rec appears_free_in x e =
|
|
match e with
|
|
| EVar y -> x = y
|
|
| EApp e1 e2 -> appears_free_in x e1 || appears_free_in x e2
|
|
| EAbs y _ e1 -> x <> y && appears_free_in x e1
|
|
| EIf e1 e2 e3 ->
|
|
appears_free_in x e1 || appears_free_in x e2 || appears_free_in x e3
|
|
| ETrue
|
|
| EFalse -> false
|
|
|
|
val free_in_context : x:int -> e:exp -> g:env -> Lemma
|
|
(requires (Some? (typing g e)))
|
|
(ensures (appears_free_in x e ==> Some? (g x)))
|
|
let rec free_in_context x e g =
|
|
match e with
|
|
| EVar _
|
|
| ETrue
|
|
| EFalse -> ()
|
|
| EAbs y t e1 -> free_in_context x e1 (extend g y t)
|
|
| EApp e1 e2 -> free_in_context x e1 g; free_in_context x e2 g
|
|
| EIf e1 e2 e3 -> free_in_context x e1 g;
|
|
free_in_context x e2 g; free_in_context x e3 g
|
|
|
|
val typable_empty_closed : x:int -> e:exp -> Lemma
|
|
(requires (Some? (typing empty e)))
|
|
(ensures (not(appears_free_in x e)))
|
|
[SMTPat (appears_free_in x e)]
|
|
let typable_empty_closed x e = free_in_context x e empty
|
|
|
|
type equal (g1:env) (g2:env) = forall (x:int). g1 x = g2 x
|
|
|
|
type equalE (e:exp) (g1:env) (g2:env) =
|
|
forall (x:int). appears_free_in x e ==> g1 x = g2 x
|
|
|
|
val context_invariance : e:exp -> g:env -> g':env -> Lemma
|
|
(requires (equalE e g g'))
|
|
(ensures (typing g e == typing g' e))
|
|
let rec context_invariance e g g' =
|
|
match e with
|
|
| EAbs x t e1 ->
|
|
context_invariance e1 (extend g x t) (extend g' x t)
|
|
|
|
| EApp e1 e2 ->
|
|
context_invariance e1 g g';
|
|
context_invariance e2 g g'
|
|
|
|
| EIf e1 e2 e3 ->
|
|
context_invariance e1 g g';
|
|
context_invariance e2 g g';
|
|
context_invariance e3 g g'
|
|
|
|
| _ -> ()
|
|
|
|
val typing_extensional : g:env -> g':env -> e:exp -> Lemma
|
|
(requires (equal g g'))
|
|
(ensures (typing g e == typing g' e))
|
|
let typing_extensional g g' e = context_invariance e g g'
|
|
|
|
val substitution_preserves_typing : x:int -> e:exp -> v:exp -> g:env -> Lemma
|
|
(requires (Some? (typing empty v) /\
|
|
Some? (typing (extend g x (Some?.v (typing empty v))) e)))
|
|
(ensures (Some? (typing empty v) /\
|
|
typing g (subst x v e) ==
|
|
typing (extend g x (Some?.v (typing empty v))) e))
|
|
let rec substitution_preserves_typing x e v g =
|
|
let Some t_x = typing empty v in
|
|
let gx = extend g x t_x in
|
|
match e with
|
|
| ETrue -> ()
|
|
| EFalse -> ()
|
|
| EVar y ->
|
|
if x=y
|
|
then context_invariance v empty g (* uses lemma typable_empty_closed *)
|
|
else context_invariance e gx g
|
|
| EApp e1 e2 ->
|
|
substitution_preserves_typing x e1 v g;
|
|
substitution_preserves_typing x e2 v g
|
|
| EIf e1 e2 e3 ->
|
|
substitution_preserves_typing x e1 v g;
|
|
substitution_preserves_typing x e2 v g;
|
|
substitution_preserves_typing x e3 v g
|
|
| EAbs y t_y e1 ->
|
|
let gxy = extend gx y t_y in
|
|
let gy = extend g y t_y in
|
|
if x=y
|
|
then typing_extensional gxy gy e1
|
|
else
|
|
(let gyx = extend gy x t_x in
|
|
typing_extensional gxy gyx e1;
|
|
substitution_preserves_typing x e1 v gy)
|
|
|
|
val preservation : e:exp -> Lemma
|
|
(requires (Some? (typing empty e) /\ Some? (step e) ))
|
|
(ensures (Some? (step e) /\
|
|
typing empty (Some?.v (step e)) == typing empty e))
|
|
let rec preservation e =
|
|
match e with
|
|
| EApp e1 e2 ->
|
|
if is_value e1
|
|
then (if is_value e2
|
|
then let EAbs x _ ebody = e1 in
|
|
substitution_preserves_typing x ebody e2 empty
|
|
else preservation e2)
|
|
else preservation e1
|
|
|
|
| EIf e1 _ _ ->
|
|
if not (is_value e1) then preservation e1
|