Definitions


pDOT Abstract Syntax and Type System


Set Implicit Arguments.

This proof uses the TLC meta-theory library by Arthur Chargueraud.
Require Export TLC.LibLN.
Require Import TLC.LibOption.
Require Import List String.

Parameter typ_label: Set.
Parameter trm_label: Set.

Abstract Syntax

Variables (x, y, z)

The proof represents variables using the locally nameless representation:
  • avar_b n represents a variable using the de Bruijn index n;
  • avar_f x represents a free variable with name x.
de Bruijn-indexed variables represent bound variables, whereas named variables represent free variables that are in the evaluation context/type environment.
Inductive avar : Set :=
  | avar_b : nat avar
  | avar_f : var avar.

Term and type members

Type member labels (A, B, C) and term (field) member labels (a, b, c).
Inductive label: Set :=
| label_typ: typ_label label
| label_trm: trm_label label.

The fields of a path in reverse order. E.g. for a path x. a1. a2... a, a list aₙ, ..., a2, a1.
Definition fields := list trm_label.

A path, x.a1.a2...a, represented through its receiver x and reversed list of fields aₙ, ..., a_2, a_1
Inductive path :=
  | p_sel : avar fields path.

Types

Types (typ, S, T, U) and type declarations (dec, D):
  • typ_top represents top;
  • typ_bot represents bottom;
  • typ_rcd d represents a record type d, where d is either a type or field declaration;
  • typ_and T U represents an intersection type T U;
  • typ_sel p A represents type selection p.A;
  • typ_bnd T represents a recursive type mu(x: T); however, since x is bound in the recursive type, it is referred to in T using the de Bruijn index 0, and is therefore omitted from the type representation; we will denote recursive types as mu(T);
  • typ_all T U represents the dependent function type (x: T)U; as in the previous case, x represents a variable bound in U, and is therefore omitted from the representation; we will denote function types as (T)U;
  • typ_sngl p denotes a singleton type p.type.
Inductive typ : Set :=
  | typ_top : typ
  | typ_bot : typ
  | typ_rcd : dec typ
  | typ_and : typ typ typ
  | typ_path : path typ_label typ
  | typ_bnd : typ typ
  | typ_all : typ typ typ
  | typ_sngl : path typ
  • dec_typ A S T represents a type declaraion {A: S..T};
  • dec_trm a T represents a field declaration {a: T} .
with dec : Set :=
  | dec_typ : typ_label typ typ dec
  | dec_trm : trm_label typ dec.

Terms

Terms (t, u), values (v), member definitions (d and defs, ds):
  • trm_val v represents a value v;
  • trm_app p q represents a function application p q;
  • trm_let t u represents a let binding let x = t in u; since x is bound in u, it is referred to in u using the de Bruijn index 0, and is therefore omitted from the let-term representation; we will denote let terms as let t in u;
  • trm_path p represents a path p.
Inductive trm : Set :=
  | trm_val : val trm
  | trm_app : path path trm
  | trm_let : trm trm trm
  | trm_path : path trm
  • val_new T ds represents the object nu(x: T)ds; the variable x is bound in T and ds and is omitted from the representation; we will denote new object definitions as nu(T)ds;
  • val_lambda T t represents a function lambda(x: T)t; again, x is bound in t and is omitted; we will denote lambda terms as lambda(T)t.
with val : Set :=
  | val_new : typ defs val
  | val_lambda : typ trm val
  • def_typ A T represents a type-member definition {A = T};
  • def_trm a t represents a field definition {a = t};
with def : Set :=
  | def_typ : typ_label typ def
  | def_trm : trm_label def_rhs def
defs represents a list of definitions that are part of an intersection
  • defs_nil represents the empty list;
  • defs_cons d ds represents a concatenation of the definition d to the definitions ds.
with defs : Set :=
  | defs_nil : defs
  | defs_cons : defs def defs

with def_rhs : Set :=
  | defp : path def_rhs
  | defv : val def_rhs.

Useful definitions and notations

  • field declarations {a: T}
Notation "'{' a '⦂' T '}'" := (dec_trm a T) (T at level 50).

  • type declarations {A: S..T}
Notation "'{' A '>:' S '<:' T '}'" := (dec_typ A S T) (S at level 58).

  • type intersection
Notation "T '∧' U" := (typ_and T U) (at level 30).

  • dependent function types ∀(x: T)U
Notation "'∀' '(' T ')' U" := (typ_all T U) (at level 30).

  • path-dependent types p.A
Notation "p '↓' A" := (typ_path p A) (at level 30).

  • recursive types μ(x: T)
Notation "'μ' T" := (typ_bnd T) (at level 30, T at level 50).

  • top type
Notation "⊤" := typ_top.

  • bottom type
Notation "⊥" := typ_bot.

singleton types p.type
Notation "'{{' p '}}'" := (typ_sngl p).

  • object ν(x: T)ds
Notation "'ν' '(' T ')' ds" := (val_new T ds) (at level 40, T at level 40, ds at level 40).

  • function λ(x: T).t
Notation "'λ' '(' T ')' t" := (val_lambda T t) (at level 50).

  • field definition {a = t} (we do not restrict terms to stable terms syntactically but the type system can only typecheck stable field definitions)
Notation "'{' a ':=' t '}'" := (def_trm a t) (t at level 50).

  • field definition with a path on the right-handside {a = p}
Notation "'{' a ':=p' p '}'" := (def_trm a (defp p)).

  • field definition with a value on the right-handside {a = v}
Notation "'{' a ':=v' v '}'" := (def_trm a (defv v)).

  • type member definition
Notation "'{' A '⦂=' T '}'" := (def_typ A T).

Shorthand definitions for variables and field selections
  • a named variable
Notation pvar x := (p_sel (avar_f x) nil).

  • a variable as a term
Notation tvar x := (trm_path (pvar x)).

  • field selection p.b on a path p
Definition sel_field (p : path) (b : trm_label) :=
  match p with
  | p_sel x bsp_sel x (b :: bs)
  end.

  • selection of multiple fields p. b1. b2... b_n where b_n, ..., b2, b1 = bs
Definition sel_fields (p : path) (bs : list trm_label) :=
  match p with
  | p_sel x bs'p_sel x (bs ++ bs')
  end.

  • field selection p.a
Notation "p '•' a" := (sel_field p a) (at level 5).

  • selection of multiple fields p.bs
Notation "p '••' bs" := (sel_fields p bs) (at level 5).

  • helper functions to retrieve labels of declarations and definitions

Definition label_of_def(d: def): label := match d with
| def_typ A _label_typ A
| { a := t }label_trm a
end.

Definition label_of_dec(D: dec): label := match D with
| { A >: _ <: _ }label_typ A
| { a _ }label_trm a
end.

Fixpoint get_def(l: label)(ds: defs): option def :=
match ds with
| defs_nilNone
| defs_cons ds' dIf label_of_def d = l then Some d else get_def l ds'
end.

Definition defs_has(ds: defs)(d: def) := get_def (label_of_def d) ds = Some d.

Definition defs_hasnt(ds: defs)(l: label) := get_def l ds = None.

Opening

Opening takes a bound variable that is represented with a de Bruijn index k and replaces it by a named variable or path u. The following functions define opening on paths, types, declarations, terms, values, and definitions.
We will denote an identifier X opened with a variable y as X^y.

Definition open_rec_avar (k: nat) (u: var) (a: avar) : avar :=
  match a with
  | avar_b iIf k = i then avar_f u else avar_b i
  | avar_f xavar_f x
end.

Definition open_rec_path (k: nat) (u: var) (p: path): path :=
  match p with
  | p_sel x bsp_sel (open_rec_avar k u x) bs
  end.

Fixpoint open_rec_typ (k: nat) (u: var) (T: typ): typ :=
  match T with
  | typ_toptyp_top
  | typ_bottyp_bot
  | typ_rcd Dtyp_rcd (open_rec_dec k u D)
  | T1 T2open_rec_typ k u T1 open_rec_typ k u T2
  | p Lopen_rec_path k u p L
  | μ Tμ (open_rec_typ (S k) u T)
  | ∀(T1) T2∀(open_rec_typ k u T1) open_rec_typ (S k) u T2
  | {{ p }}{{ open_rec_path k u p }}
  end
with open_rec_dec (k: nat) (u: var) (D: dec): dec :=
  match D with
  | { A >: T <: U }{ A >: open_rec_typ k u T <: open_rec_typ k u U }
  | { a T }{ a open_rec_typ k u T }
  end.

Fixpoint open_rec_trm (k: nat) (u: var) (t: trm): trm :=
  match t with
  | trm_val vtrm_val (open_rec_val k u v)
  | trm_path ptrm_path (open_rec_path k u p)
  | trm_app p qtrm_app (open_rec_path k u p) (open_rec_path k u q)
  | trm_let t1 t2trm_let (open_rec_trm k u t1) (open_rec_trm (S k) u t2)
  end
with open_rec_val (k: nat) (u: var) (v: val): val :=
  match v with
  | ν(T)dsν (open_rec_typ (S k) u T) open_rec_defs (S k) u ds
  | λ(T) eλ(open_rec_typ k u T) open_rec_trm (S k) u e
  end
with open_rec_def (k: nat) (u: var) (d: def): def :=
  match d with
  | def_typ A Tdef_typ A (open_rec_typ k u T)
  | { a := t }{ a := open_rec_defrhs k u t }
  end
with open_rec_defs (k: nat) (u: var) (ds: defs): defs :=
  match ds with
  | defs_nildefs_nil
  | defs_cons tl ddefs_cons (open_rec_defs k u tl) (open_rec_def k u d)
  end
with open_rec_defrhs (k: nat) (u: var) (drhs: def_rhs) : def_rhs :=
  match drhs with
  | defp pdefp (open_rec_path k u p)
  | defv vdefv (open_rec_val k u v)
  end.

Definition open_avar u a := open_rec_avar 0 u a.
Definition open_typ u t := open_rec_typ 0 u t.
Definition open_dec u D := open_rec_dec 0 u D.
Definition open_trm u e := open_rec_trm 0 u e.
Definition open_val u v := open_rec_val 0 u v.
Definition open_def u d := open_rec_def 0 u d.
Definition open_defs u l := open_rec_defs 0 u l.
Definition open_defrhsdd u t := open_rec_defrhs 0 u t.
Definition open_path u p := open_rec_path 0 u p.

Definition open_paths u ps := map (open_path u) ps.

Path opening replaces in some syntax a bound variable with dangling index (k) by a path p.
Opening of variables
Definition open_rec_avar_p (k: nat) (u: path) (a: avar) : path :=
  match a with
  | avar_b iIf k = i then u else p_sel (avar_b i) nil
  | avar_f xpvar x
  end.

Opening of paths
Example:
0.a.b ^ y.c.d == y.c.d.a.b
our representation:
0 [b, a] ^ y [d, c] == y [b, a, d, c]
Definition open_rec_path_p (k: nat) (u: path) (p: path): path :=
  match p, u with
  | p_sel x bs, p_sel y cs
    match x with
    | avar_b iIf k = i then p_sel y (bs ++ cs) else p (* maintaining reverse order of fields *)
    | avar_f xp
    end
  end.

Fixpoint open_rec_typ_p (k: nat) (u: path) (T: typ): typ :=
  match T with
  | typ_toptyp_top
  | typ_bottyp_bot
  | typ_rcd Dtyp_rcd (open_rec_dec_p k u D)
  | T1 T2open_rec_typ_p k u T1 open_rec_typ_p k u T2
  | p Lopen_rec_path_p k u p L
  | μ Tμ (open_rec_typ_p (S k) u T)
  | ∀(T1) T2∀(open_rec_typ_p k u T1) open_rec_typ_p (S k) u T2
  | {{ p }}{{ open_rec_path_p k u p }}
  end
with open_rec_dec_p (k: nat) (u: path) (D: dec): dec :=
  match D with
  | { A >: T <: U }{ A >: open_rec_typ_p k u T <: open_rec_typ_p k u U }
  | { a T }{ a open_rec_typ_p k u T }
  end.

Fixpoint open_rec_trm_p (k: nat) (u: path) (t: trm): trm :=
  match t with
  | trm_val vtrm_val (open_rec_val_p k u v)
  | trm_path ptrm_path (open_rec_path_p k u p)
  | trm_app p qtrm_app (open_rec_path_p k u p) (open_rec_path_p k u q)
  | trm_let t1 t2trm_let (open_rec_trm_p k u t1) (open_rec_trm_p (S k) u t2)
  end
with open_rec_val_p (k: nat) (u: path) (v: val): val :=
  match v with
  | ν(T) dsν(open_rec_typ_p (S k) u T) open_rec_defs_p (S k) u ds
  | λ(T) eλ(open_rec_typ_p k u T) open_rec_trm_p (S k) u e
  end
with open_rec_def_p (k: nat) (u: path) (d: def): def :=
  match d with
  | def_typ L Tdef_typ L (open_rec_typ_p k u T)
  | { a := t }{ a := open_rec_defrhs_p k u t }
  end
with open_rec_defs_p (k: nat) (u: path) (ds: defs): defs :=
  match ds with
  | defs_nildefs_nil
  | defs_cons tl ddefs_cons (open_rec_defs_p k u tl) (open_rec_def_p k u d)
  end
with open_rec_defrhs_p (k: nat) (u: path) (drhs: def_rhs) : def_rhs :=
  match drhs with
  | defp pdefp (open_rec_path_p k u p)
  | defv vdefv (open_rec_val_p k u v)
  end.

Definition open_avar_p u a := open_rec_avar_p 0 u a.
Definition open_typ_p u t := open_rec_typ_p 0 u t.
Definition open_dec_p u D := open_rec_dec_p 0 u D.
Definition open_path_p u p := open_rec_path_p 0 u p.
Definition open_trm_p u t := open_rec_trm_p 0 u t.
Definition open_val_p u v := open_rec_val_p 0 u v.
Definition open_def_p u d := open_rec_def_p 0 u d.
Definition open_defs_p u l := open_rec_defs_p 0 u l.
Definition open_defrhs_p u t := open_rec_defrhs_p 0 u t.

Path Replacement

repl_typ p q T U represents the fact that T[q/p] = U; more precisely,
  • T has a path p.bs that starts with p
  • replacing that occurrence of p with q yields the type U

Inductive repl_typ : path path typ typ Prop :=
| rrcd: p q D1 D2,
    repl_dec p q D1 D2
    repl_typ p q (typ_rcd D1) (typ_rcd D2)
| rand1: p q T1 T2 U,
    repl_typ p q T1 T2
    repl_typ p q (T1 U) (T2 U)
| rand2: p q T1 T2 U,
    repl_typ p q T1 T2
    repl_typ p q (U T1) (U T2)
| rpath: p q bs A,
    repl_typ p q (p••bs A) (q••bs A)
| rbnd: p q T1 T2,
    repl_typ p q T1 T2
    repl_typ p q (μ T1) (μ T2)
| rall1: p q T1 T2 U,
    repl_typ p q T1 T2
    repl_typ p q (∀(T1) U) (∀(T2) U)
| rall2: p q T1 T2 U,
    repl_typ p q T1 T2
    repl_typ p q (∀(U) T1) (∀(U) T2)
| rsngl: p q bs,
    repl_typ p q {{ p••bs }} {{ q••bs }}
with repl_dec : path path dec dec Prop :=
| rdtyp1: p q T1 T2 A U,
    repl_typ p q T1 T2
    repl_dec p q {A >: T1 <: U} {A >: T2 <: U}
| rdtyp2: p q T1 T2 A U,
    repl_typ p q T1 T2
    repl_dec p q {A >: U <: T1} {A >: U <: T2}
| rdtrm: p q T1 T2 a,
    repl_typ p q T1 T2
    repl_dec p q {a T1} {a T2}.

Hint Constructors repl_typ repl_dec.

Free variables

Functions that retrieve the free variables of a symbol.
Free variable in a variable.
Definition fv_avar (a: avar) : vars :=
  match a with
  | avar_b i\{}
  | avar_f x\{x}
  end.

Free variables in a path.
Fixpoint fv_path (p: path) : vars :=
  match p with
  | p_sel x bsfv_avar x
  end.

Free variables in a type or declaration.
Fixpoint fv_typ (T: typ) : vars :=
  match T with
  | typ_top\{}
  | typ_bot\{}
  | typ_rcd Dfv_dec D
  | T U(fv_typ T) \u (fv_typ U)
  | p Lfv_path p
  | μ Tfv_typ T
  | ∀(T1) T2(fv_typ T1) \u (fv_typ T2)
  | {{ p }}fv_path p
  end
with fv_dec (D: dec) : vars :=
  match D with
  | { A >: T <: U }(fv_typ T) \u (fv_typ U)
  | { a T }fv_typ T
  end.

Free variables in a term, value, or definition.
Fixpoint fv_trm (t: trm) : vars :=
  match t with
  | trm_val v ⇒ (fv_val v)
  | trm_path p ⇒ (fv_path p)
  | trm_app p q(fv_path p) \u (fv_path q)
  | trm_let t1 t2(fv_trm t1) \u (fv_trm t2)
  end
with fv_val (v: val) : vars :=
  match v with
  | ν(T) ds(fv_typ T) \u (fv_defs ds)
  | λ(T)t(fv_typ T) \u (fv_trm t)
  end
with fv_def (d: def) : vars :=
  match d with
  | def_typ _ T ⇒ (fv_typ T)
  | { _ := t } ⇒ (fv_defrhs t)
  end
with fv_defs(ds: defs) : vars :=
  match ds with
  | defs_nil\{}
  | defs_cons tl d(fv_defs tl) \u (fv_def d)
  end
with fv_defrhs(drhs: def_rhs) : vars :=
  match drhs with
  | defp pfv_path p
  | defv vfv_val v
  end.

Typing environment (G)
Definition ctx := env typ.

The sequence of variable-to-value let bindings, (let x = v in)*, is represented as a value environment that maps variables to values:
Definition sta := env val.

Free variables in the range (types) of a context
Definition fv_ctx_types(G: ctx): vars :=
  (fv_in_values (fun Tfv_typ T) G).
Definition fv_sta_vals(s: sta): vars := (fv_in_values (fun vfv_val v) s).

Record and Inert Types

A record declaration is either a type declaration with equal bounds, or a field declaration.
Inductive record_dec : dec Prop :=
| rd_typ : A T, record_dec { A >: T <: T }
| rd_trm : a T, inert_typ T record_dec { a T }
| rd_trm_sngl : a p, record_dec { a {{ p }} }

Given a record declaration, a record_typ keeps track of the declaration's field member labels (i.e. names of fields) and type member labels (i.e. names of abstract type members). record_typ also requires that the labels are distinct.
with record_typ : typ fset label Prop :=
| rt_one : D l,
  record_dec D
  l = label_of_dec D
  record_typ (typ_rcd D) \{l}
| rt_cons: T ls D l,
  record_typ T ls
  record_dec D
  l = label_of_dec D
  l \notin ls
  record_typ (T typ_rcd D) (union ls \{l})

A type is inert if it is either a dependent function type, or a recursive type μ(x: T) where T is a record type. For example, the following types are inert:
  • lambda(x: S)T
  • mu(x: {a: T} {B: U..U})
  • mu(x: {C: {A: T..U}..{A: T..U}})
  • mu(x: {a: μ(y: {A: T..T})})
And the following types are not inert:
  • {a: T}
  • {B: U..U}
  • top
  • x.A
  • mu(x: {B: S..T}), where S T
  • mu(x: {a: μ(y: {A: S..T})}), where S T
with inert_typ : typ Prop :=
  | inert_typ_all : S T, inert_typ (∀(S) T)
  | inert_typ_bnd : T ls,
      record_typ T ls
      inert_typ (μ T).

Given a type T = D1 D2 ... Dn and member declaration D, record_has T D tells whether D is contained in the intersection of Di's.
Inductive record_has: typ dec Prop :=
| rh_one : D,
    record_has (typ_rcd D) D
| rh_andl : T U D,
    record_has T D
    record_has (T U) D
| rh_andr : T U D,
    record_has U D
    record_has (T U) D.

A record_type is a record_typ with an unspecified set of labels. The meaning of record_type is an intersection of type/field declarations with distinct labels.
Definition record_type T := ls, record_typ T ls.

An inert context is a typing context whose range consists only of inert types.
Inductive inert : ctx Prop :=
  | inert_empty : inert empty
  | inert_push : G x T,
      inert G
      inert_typ T
      x # G
      inert (G & x ~ T).

Typing Rules

The tight_bounds function ensures that all type declarations nested inside a type have equal bounds (except the ones that are inside of function types)
Fixpoint tight_bounds T :=
  match T with
  | typ_rcd Dtight_bounds_dec D
  | U Vtight_bounds U tight_bounds V
  | typ_bnd Utight_bounds U
  | _True
  end
with tight_bounds_dec D :=
  match D with
  | { a T }tight_bounds T
  | { A >: T <: U }T = U
  end.

Reserved Notation "G '⊢' t ':' T" (at level 40, t at level 59).
Reserved Notation "G '⊢' T '<:' U" (at level 40, T at level 59).
Reserved Notation "x ';' bs ';' G '⊢' d ':' D"
         (at level 40, bs at level 39, G at level 39, d at level 59).
Reserved Notation "x ';' bs ';' G '⊢' ds '::' D"
         (at level 40, bs at level 39, G at level 39, ds at level 59).

Term typing G t: T

Inductive ty_trm : ctx trm typ Prop :=


G(x) = T
________
Gx: T
| ty_var : x T G,
    binds x T G
    G tvar x : T


G, z: Tt^z: U^z
z fresh
__________________
Glambda(T)t: (T)U
| ty_all_intro : L G T t U,
    ( z, z \notin L
      G & z ~ T open_trm z t : open_typ z U)
    G trm_val (λ(T) t) : ∀(T) U


Gp: (S)T
Gq: S
_________________
Gp q: T^q
| ty_all_elim : G p q S T,
    G trm_path p : ∀(S) T
    G trm_path q : S
    G trm_app p q : open_typ_p q T


z; G, z: T^zds^z :: T^z
z fresh
_________________
Gnu(T)ds :: mu(T)
| ty_new_intro : L G T ds,
    ( z, z \notin L
      z; nil; G & (z ~ open_typ z T) open_defs z ds :: open_typ z T)
    G trm_val (ν(T) ds) : μ T


Gp: {a: T}
_________________
Gp.a: T
| ty_new_elim : G p a T,
    G trm_path p : typ_rcd { a T }
    G trm_path pa : T


Gp.a: T
_________________
Gp: {a: T}
| ty_rcd_intro : G p a T,
    G trm_path pa : T
    G trm_path p : typ_rcd { a T }


Gt: T
G, x: Tu^x: U
x fresh
_________________
Glet t in u: U
| ty_let : L G t u T U,
    G t : T
    ( x, x \notin L
      G & x ~ T open_trm x u : U)
    G trm_let t u : U


Gp: q.type
Gq: T
_________________
Gp: T
| ty_sngl : G p q T,
    G trm_path p : {{ q }}
    G trm_path q : T
    G trm_path p : T


Gp: q.type
Gq.a
_________________
Gp.a: q.a.type
| ty_path_elim : G p q a T,
    G trm_path p : {{ q }}
    G trm_path qa : T
    G trm_path pa : {{ qa }}


Gp: T^p
_________________
Gp: mu(T)
| ty_rec_intro : G p T,
    G trm_path p : open_typ_p p T
    G trm_path p : μ T


Gp: mu(T)
_________________
Gp: T^p
| ty_rec_elim : G p T,
    G trm_path p : μ T
    G trm_path p : open_typ_p p T


Gp: T
Gp: U
_________________
Gp: TU
| ty_and_intro : G p T U,
    G trm_path p : T
    G trm_path p : U
    G trm_path p : T U


Gt: T
GT <: U
_________________
Gt: U
| ty_sub : G t T U,
    G t : T
    G T <: U
    G t : U
where "G '⊢' t ':' T" := (ty_trm G t T)

Single-definition typing x; bs; G d: D

The notation x; bs; G d : D denotes the typing of a definition d with record-type D under the path x.bs (i.e. a path with receiver x and fields bs)
with ty_def : var fields ctx def dec Prop :=
x.bs; G {A = T}: {A: T..T}
| ty_def_typ : x bs G A T,
    x; bs; G def_typ A T : { A >: T <: T }


Glambda(T)t: ∀(U)V
_________________
x.bs; G ⊢ {b = lambda(T)t}: {b: ∀(U)V}
 | ty_def_all : x bs G T t b U V,
    G trm_val (λ(T) t) : ∀(U) V
    x; bs; G { b :=v λ(T) t } : { b ∀(U) V }


x.bs.b; Gds^x.bs.b: T^x.bs.b
_________________
x.bs; G ⊢ {b = nu(T)ds}: {b: T}
 | ty_def_new : x bs b G ds T p,
     p = p_sel (avar_f x) bs
     tight_bounds (μ T)
     x; (b :: bs); G open_defs_p pb ds :: open_typ_p pb T
     x; bs; G { b :=v ν(T) ds } : { b μ T }


Gq
_____________________________
x.bs; G ⊢ {b = q}: {b: q.type}
 | ty_def_path : x bs G q b T,
    G trm_path q: T
    x; bs; G { b :=p q } : { b {{ q }} }

where "x ';' bs ';' G '⊢' d ':' D" := (ty_def x bs G d D)

Multiple-definition typing x; bs; G ds :: T

The notation x; bs; G ds :: T denotes the typing of definitions ds with type T under the path x.bs (i.e. a path with receiver x and fields bs)
with ty_defs : var fields ctx defs typ Prop :=

x.bs; Gd: D
___________________________
x.bs; Gd ++ defs_nil : D
| ty_defs_one : x bs G d D,
    x; bs; G d : D
    x; bs; G defs_cons defs_nil d :: typ_rcd D


x.bs; Gds :: T
x.bs; Gd: D
dds
__________________________
x.bs; Gds ++ d : TD
| ty_defs_cons : x bs G d ds D T,
    x; bs; G ds :: T
    x; bs; G d : D
    defs_hasnt ds (label_of_def d)
    x; bs; G defs_cons ds d :: T typ_rcd D
where "x ';' bs ';' G '⊢' ds '::' T" := (ty_defs x bs G ds T)

Subtyping G T <: U

with subtyp : ctx typ typ Prop :=


GT <: top
| subtyp_top: G T,
    G T <: typ_top


Gbot <: T
| subtyp_bot: G T,
    G typ_bot <: T


GT <: T
| subtyp_refl: G T,
    G T <: T


GS <: T
GT <: U
_________________
GS <: U
| subtyp_trans: G S T U,
    G S <: T
    G T <: U
    G S <: U


GTU <: T
| subtyp_and11: G T U,
    G T U <: T

G T U <: U
| subtyp_and12: G T U,
    G T U <: U


GS <: T
GS <: U
_________________
GS <: TU
| subtyp_and2: G S T U,
    G S <: T
    G S <: U
    G S <: T U


GT <: U
_________________
G ⊢ {a: T} <: {a: U}
| subtyp_fld: G T U a,
    G T <: U
    G typ_rcd { a T } <: typ_rcd { a U }


GS2 <: S1
GT1 <: T2
_________________
G ⊢ {A: S1..T1} <: {A: S2..T2}
| subtyp_typ: G S1 S2 T1 T2 A,
    G S2 <: S1
    G T1 <: T2
    G typ_rcd { A >: S1 <: T1 } <: typ_rcd { A >: S2 <: T2 }


Gp: q.type
Gq
_________________
GT <: T[q/p, n]
| subtyp_sngl_pq : G p q T T' U,
    G trm_path p : {{ q }}
    G trm_path q : U
    repl_typ p q T T'
    G T <: T'


Gp: q.type
Gq
_________________
GT <: T[p/q, n]
| subtyp_sngl_qp : G p q T T' U,
    G trm_path p : {{ q }}
    G trm_path q : U
    repl_typ q p T T'
    G T <: T'


Gp: {A: S..T}
_________________
GS <: p.A
| subtyp_sel2: G p A S T,
    G trm_path p : typ_rcd { A >: S <: T }
    G S <: (p A)


Gp: {A: S..T}
_________________
Gp.A <: T
| subtyp_sel1: G p A S T,
    G trm_path p : typ_rcd { A >: S <: T }
    G p A <: T


GS2 <: S1
G, x: S2T1^x <: T2^x
x fresh
_________________
G(S1)T1 <: (S2)T2
| subtyp_all : (L : fset var) (G : ctx) (S1 T1 S2 T2 : typ),
    G S2 <: S1
    ( x : var, x \notin L G & x ~ S2 open_typ x T1 <: open_typ x T2)
      G ∀(S1) T1 <: ∀(S2) T2
where "G '⊢' T '<:' U" := (subtyp G T U).

Well-typed stores

The operational semantics is defined in terms of pairs (s, t), where s] is a store (runtime environment) and t is a term. Given a typing G (s, t): T, well_typed establishes a correspondence between G and the store s.
We say that s is well-typed with respect to G if
  • G = {(xi mapsto Ti) | i = 1, ..., n}
  • s = {(xi mapsto vi) | i = 1, ..., n}
  • G vi: Ti.
We say that e is well-typed with respect to G, denoted as s: G.

Reserved Notation "γ '⫶' G" (at level 40).

Inductive well_typed: ctx sta Prop :=
| well_typed_empty: empty empty
| well_typed_push: G γ x T v,
    γ G
    x # G
    x # γ
    G trm_val v : T
    γ & x ~ v G & x ~ T
where "γ ⫶ G" := (well_typed G γ).

Infrastructure


Hint Constructors
     inert_typ inert record_has record_dec record_typ
     ty_trm ty_def ty_defs subtyp well_typed.

Hint Unfold record_type.

Mutual Induction Principles


Scheme typ_mut := Induction for typ Sort Prop
  with dec_mut := Induction for dec Sort Prop.
Combined Scheme typ_mutind from typ_mut, dec_mut.

Scheme trm_mut := Induction for trm Sort Prop
  with val_mut := Induction for val Sort Prop
  with def_mut := Induction for def Sort Prop
  with defs_mut := Induction for defs Sort Prop
  with defrhs_mut := Induction for def_rhs Sort Prop.
Combined Scheme trm_mutind from trm_mut, val_mut, def_mut, defs_mut, defrhs_mut.

Scheme ty_trm_mut := Induction for ty_trm Sort Prop
  with ty_def_mut := Induction for ty_def Sort Prop
  with ty_defs_mut := Induction for ty_defs Sort Prop.
Combined Scheme ty_mutind from ty_trm_mut, ty_def_mut, ty_defs_mut.

Scheme tds_ty_trm_mut := Induction for ty_trm Sort Prop
  with tds_ty_def_mut := Induction for ty_def Sort Prop
  with tds_ty_defs_mut := Induction for ty_defs Sort Prop
  with tds_subtyp := Induction for subtyp Sort Prop.
Combined Scheme tds_mutind from tds_ty_trm_mut, tds_ty_def_mut, tds_ty_defs_mut, tds_subtyp.

Scheme ts_ty_trm_mut := Induction for ty_trm Sort Prop
  with ts_subtyp := Induction for subtyp Sort Prop.
Combined Scheme ts_mutind from ts_ty_trm_mut, ts_subtyp.

Scheme rules_trm_mut := Induction for ty_trm Sort Prop
  with rules_def_mut := Induction for ty_def Sort Prop
  with rules_defs_mut := Induction for ty_defs Sort Prop
  with rules_subtyp := Induction for subtyp Sort Prop.
Combined Scheme rules_mutind from rules_trm_mut, rules_def_mut, rules_defs_mut, rules_subtyp.

Scheme rcd_dec_mut := Induction for record_dec Sort Prop
  with rcd_typ_mut := Induction for record_typ Sort Prop
  with inert_mut := Induction for inert_typ Sort Prop.
Combined Scheme rcd_mutind from rcd_dec_mut, rcd_typ_mut, inert_mut.

Scheme repl_typ_mut := Induction for repl_typ Sort Prop
  with repl_dec_mut := Induction for repl_dec Sort Prop.
Combined Scheme repl_mutind from repl_typ_mut, repl_dec_mut.

Scheme ty_def_mut' := Induction for ty_def Sort Prop
  with ty_defs_mut' := Induction for ty_defs Sort Prop.
Combined Scheme ty_def_mutind from ty_def_mut', ty_defs_mut'.

Tactics

Tactics for generating fresh variables.

Ltac gather_vars :=
  let A := gather_vars_with (fun x : varsx ) in
  let B := gather_vars_with (fun x : var\{ x } ) in
  let C := gather_vars_with (fun x : ctx(dom x) \u (fv_ctx_types x)) in
  let D := gather_vars_with (fun x : stadom x \u fv_sta_vals x) in
  let E := gather_vars_with (fun x : avarfv_avar x) in
  let F := gather_vars_with (fun x : trmfv_trm x) in
  let G := gather_vars_with (fun x : valfv_val x) in
  let H := gather_vars_with (fun x : deffv_def x) in
  let I := gather_vars_with (fun x : defsfv_defs x) in
  let J := gather_vars_with (fun x : typfv_typ x) in
  let K := gather_vars_with (fun x : def_rhsfv_defrhs x) in
  let L := gather_vars_with (fun x : pathfv_path x)
in
  constr:(A \u B \u C \u D \u E \u F \u G \u H \u I \u J \u K \u L).

Ltac pick_fresh x :=
  let L := gather_vars in (pick_fresh_gen L x).

Tactic Notation "apply_fresh" constr(T) "as" ident(x) :=
  apply_fresh_base T gather_vars x.

Ltac fresh_constructor :=
  apply_fresh ty_new_intro as z ||
  apply_fresh ty_all_intro as z ||
  apply_fresh ty_let as z ||
  apply_fresh subtyp_all as z; auto.

Tactics for naming cases in case analysis.

Open Scope string_scope.

Ltac move_to_top x :=
  match reverse goal with
  | H : _ |- _try move x after H
  end.

Tactic Notation "assert_eq" ident(x) constr(v) :=
  let H := fresh in
  assert (x = v) as H by reflexivity;
  clear H.

Tactic Notation "Case_aux" ident(x) constr(name) :=
  first [
    set (x := name); move_to_top x
  | assert_eq x name; move_to_top x
  | fail 1 "because we are working on a different case" ].

Tactic Notation "Case" constr(name) := Case_aux Case name.
Tactic Notation "SCase" constr(name) := Case_aux SCase name.
Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.

Automatically destruct premises
Ltac destruct_all :=
  repeat match goal with
  | [ H : x, _ |- _ ] ⇒ destruct H
  | [ H : ?A ?B |- _ ] ⇒ destruct H
  | [ H : ?A ?B |- _ ] ⇒ destruct H
         end.

Ltac destruct_notin :=
  repeat match goal with
         | [ H: ?z \notin ?E1 \u ?E2 |- _ ] ⇒
           apply notin_union in H; destruct H
         end.