From stdpp Require Import strings.
From iris.base_logic Require Import lib.iprop.
Require uses context.
Import uses.
Module Type expr_spec.
Parameter expr : ∀ {Σ}, iProp Σ -> Type.
Parameter check_program : ∀ {Σ} {type : iProp Σ}, expr type -> Prop.
Axiom check_program_spec : ∀ {Σ} {type : iProp Σ} (e : expr type),
check_program e -> ⊢ type.
End expr_spec.
Module expr (context : context.context_spec uses).
Inductive exprT {Σ : gFunctors} : iProp Σ -> Type :=
| Var {T} : string -> exprT T
| Lam {T1 T2} : string -> exprT T2 -> exprT (T1 -∗ T2)
| Pair {T1 T2} : exprT T1 -> exprT T2 -> exprT (T1 ∗ T2)
.
Definition expr {Σ} := exprT (Σ := Σ).
Fixpoint check_expr {Σ} {type : iProp Σ} (e : expr type) : context.context Σ := match e with
| Var var => context.var var type
| @Lam _ T1 T2 var e =>
let ctx := check_expr e in
context.constrain
(match context.lookup var ctx with
| Some (T, uses) => T = T1 ∧ uses = uses.One
| None => True
end)
(context.bind var (check_expr e))
| Pair e1 e2 => context.sep (check_expr e1) (check_expr e2)
end.
Lemma check_expr_spec {Σ} (type : iProp Σ) (e : expr type) :
context.prop (check_expr e) ⊢ type.
Proof.
elim e; simpl.
- move=> T var. apply: context.var_spec.
- move=> T1 T2 var body H.
apply: transitivity; first apply: context.constrain_spec.
apply: bi.exist_elim => w.
apply: bi.wand_intro_r.
apply: transitivity; last apply: H.
apply: transitivity; last apply: (context.bind_spec var).
rewrite {1}bi.sep_comm.
apply: bi.sep_mono_l.
move: w; case: (context.lookup var (check_expr body)) => [[P u]|].
+ by move=>[->->].
+ move=>[]. apply: bi.True_intro.
- move=> T1 T2 e1 H1 e2 H2.
apply: transitivity; first apply: context.sep_spec.
by apply: bi.sep_mono.
Qed.
Definition check_program {Σ} {type : iProp Σ} (e : expr type) : Prop :=
context.toplevel (check_expr e).
Theorem check_program_spec {Σ} {type : iProp Σ} (e : expr type) :
check_program e -> ⊢ type.
Proof.
move=> w.
apply: transitivity.
- apply: context.toplevel_spec w.
- apply: check_expr_spec.
Qed.
End expr.