Difference refinements and source proof-carrying code
appendix sectionrules
Difference refinements and source proof-carrying code
The system of chapter 10 is a separate, nondependent calculus. Its base types, values, atoms, computations, and expressions are 𝐵::=𝗂𝗇𝗍∣𝖺𝗋𝗋,𝑣::=𝑛∣⟨𝑛0,…,𝑛𝑘−1⟩∣(𝜆𝑥.𝑒:𝑥:𝑠→𝑡)∣(𝖿𝗂𝗑𝑓(𝑥).𝑒:𝑥:𝑠→𝑡),𝑎::=𝑥∣𝑣,𝑐::=𝑎+𝑘∣𝗅𝖾𝗇𝑎∣𝗀𝖾𝗍𝑎1𝑎2∣𝑎1𝑎2,𝑒::=𝑎∣𝗅𝖾𝗍𝑥=𝑐𝗂𝗇𝑒∣𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2∣𝖾𝗋𝗋𝗈𝗋. Here 𝑘,𝑛,𝑛𝑖∈ℤ. Terms are A-normal by grammar. Bind composition, with capture-avoiding renaming before its let clause, is 𝑎▹𝑥𝑒2=𝑒2[𝑎/𝑥],(𝗅𝖾𝗍𝑦=𝑐𝗂𝗇𝑒)▹𝑥𝑒2=𝗅𝖾𝗍𝑦=𝑐𝗂𝗇(𝑒▹𝑥𝑒2),(𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒0𝖾𝗅𝗌𝖾𝑒1)▹𝑥𝑒2=𝗂𝖿𝛿𝗍𝗁𝖾𝗇(𝑒0▹𝑥𝑒2)𝖾𝗅𝗌𝖾(𝑒1▹𝑥𝑒2),𝖾𝗋𝗋𝗈𝗋▹𝑥𝑒2=𝖾𝗋𝗋𝗈𝗋. Reduction is on closed terms. For 𝐴=⟨𝑛0,…,𝑛𝑚−1⟩ its complete root table is
𝑞=𝑛+𝑘inℤ
𝗅𝖾𝗍𝑥=𝑛+𝑘𝗂𝗇𝑒⟼𝑒[𝑞/𝑥]
E-Shift
𝗅𝖾𝗍𝑥=𝗅𝖾𝗇𝐴𝗂𝗇𝑒⟼𝑒[𝑚/𝑥]
E-Len
0≤𝑖<𝑚
𝗅𝖾𝗍𝑥=𝗀𝖾𝗍𝐴𝑖𝗂𝗇𝑒⟼𝑒[𝑛𝑖/𝑥]
E-Get
𝗅𝖾𝗍𝑦=(𝜆𝑥.𝑒1:𝑥:𝑠→𝑡)𝑣𝗂𝗇𝑒2⟼𝑒1[𝑣/𝑥]▹𝑦𝑒2
E-Beta
𝐹=(𝖿𝗂𝗑𝑓(𝑥).𝑒1:𝑥:𝑠→𝑡)
𝗅𝖾𝗍𝑦=𝐹𝑣𝗂𝗇𝑒2⟼𝑒1[𝐹/𝑓,𝑣/𝑥]▹𝑦𝑒2
E-Fix
𝛿istrue
𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2⟼𝑒1
E-IfT
𝛿isfalse
𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2⟼𝑒2
E-IfF
There is no reduction root for an out-of-bounds get and no congruence relation: the grammar exposes the next computation at the head of a let.
Predicate vertices, atoms, conjunctions, and types are 𝑟::=𝟎∣𝑥∣𝐿𝑎,𝛿::=𝑟−𝑠≤𝑘,𝑝::=𝗍𝗋𝗎𝖾∣𝛿∧𝑝,𝑡::={𝜈:𝐵∣𝑝}∣𝑥:𝑠→𝑡.𝐿𝑎 abbreviates 𝗅𝖾𝗇(𝑎). An integer declaration contributes 𝑥 to the vertex scope, an array declaration contributes 𝐿𝑥, and a function declaration contributes no vertex. Put 𝜇𝗂𝗇𝗍(𝜈)=𝜈 and 𝜇𝖺𝗋𝗋(𝜈)=𝐿𝜈. The complement of one integer atom is ――――――𝑟−𝑠≤𝑘:=𝑠−𝑟≤−𝑘−1. Literal substitution replaces an integer literal 𝑛 by 𝟎+𝑛 and an array literal of length 𝑚 by 𝟎+𝑚, then moves constants to the right side of ≤. Contexts and their complete formation table are
⋅𝖼𝗍𝗑
WF-Empty
Γ𝖼𝗍𝗑Γ⊢𝑡𝗍𝗒𝗉𝖾𝑥∉dom(Γ)
Γ,𝑥:𝑡𝖼𝗍𝗑
WF-Var
Γ𝖼𝗍𝗑𝛿over𝑉(Γ)
Γ,𝛿𝖼𝗍𝗑
WF-Guard
Γ𝖼𝗍𝗑𝑝over𝑉(Γ)∪{𝜇𝐵(𝜈)}
Γ⊢{𝜈:𝐵∣𝑝}𝗍𝗒𝗉𝖾
WF-Base
Γ⊢𝑠𝗍𝗒𝗉𝖾Γ,𝑥:𝑠⊢𝑡𝗍𝗒𝗉𝖾
Γ⊢𝑥:𝑠→𝑡𝗍𝗒𝗉𝖾
WF-Arrow
The shape operation is |{𝜈:𝐵∣𝑝}|=𝐵 and |𝑥:𝑠→𝑡|=|𝑠|→|𝑡|.
The context embedding and semantic entailment are ⌊⋅⌋=𝗍𝗋𝗎𝖾,⌊Γ,𝑥:{𝜈:𝐵∣𝑝}⌋=⌊Γ⌋∧𝑝[𝑥/𝜈],⌊Γ,𝑥:(𝑦:𝑠→𝑡)⌋=⌊Γ⌋,⌊Γ,𝛿⌋=⌊Γ⌋∧𝛿. A valuation maps integer variables to integers and array variables to finite integer arrays, interprets 𝐿𝑎 by array length, and satisfies Γ when it satisfies ⌊Γ⌋. The judgment Γ⊧𝑝 means that every such valuation satisfying Γ satisfies 𝑝.
For certificate replay, 𝐺Γ has vertices 𝟎 and 𝑉(Γ). Every replayed goal atom is required to use only those vertices. Each hypothesis 𝑟−𝑠≤𝑘 contributes an identified edge 𝑠𝑘→𝑟, and every array vertex contributes the implicit edge 𝐿𝑎0→𝟎. A path certificate for 𝑟−𝑠≤𝑘 is a possibly empty adjacent edge list from 𝑠 to 𝑟 with weight sum at most 𝑘. A contradiction certificate is a nonempty adjacent cyclic edge list of negative sum. The replay checker re-reads the identified edges, verifies adjacency and endpoints, adds weights in ℤ, and checks the final bound. A conjunctive goal accepts one contradiction certificate or one path certificate per conjunct. These checks, rather than an external solver’s answer, are the certificate judgment.
Subtyping has exactly two rules. Every displayed type is well formed; 𝑧 is fresh in the base rule, and arrow binders are alpha-aligned.
𝑧∉dom(Γ)Γ,𝑧:{𝜈:𝐵∣𝑝}⊧𝑞[𝑧/𝜈]
Γ⊢{𝜈:𝐵∣𝑝}<:{𝜈:𝐵∣𝑞}
S-Base
Γ⊢𝑠1<:𝑡1Γ,𝑥:𝑠1⊢𝑡2<:𝑠2
Γ⊢(𝑥:𝑡1→𝑡2)<:(𝑥:𝑠1→𝑠2)
S-Arrow
For an integer atom, (𝑥,0) and (𝟎,𝑛) are its representatives; for an array atom, (𝐿𝑎,0) and (𝟎,𝑚) are its length representatives. The exact result constructors and get precondition are 𝖤𝗊𝖨(𝑟,𝑘)={𝜈:𝗂𝗇𝗍∣𝜈−𝑟≤𝑘∧𝑟−𝜈≤−𝑘},𝖲𝗁𝗂𝖿𝗍(𝑎,𝑗)=𝖤𝗊𝖨(𝑟,𝑘+𝑗),𝖫𝖾𝗇𝗀𝗍𝗁(𝑎)=𝖤𝗊𝖨(𝑟,𝑘),𝖠𝗋𝗋𝖺𝗒𝑚={𝜈:𝖺𝗋𝗋∣𝐿𝜈−𝟎≤𝑚∧𝟎−𝐿𝜈≤−𝑚},𝖡𝗇𝖽(𝑎,𝑖)=(𝟎−𝑟𝑖≤𝑘𝑖)∧(𝑟𝑖−𝑟𝑎≤𝑘𝑎−𝑘𝑖−1). The second through fourth lines use the representative appropriate to their argument. Atoms and computations synthesize; expressions check. The complete declarative table is
𝑥:𝑡∈Γ
Γ⊢𝑥⇒𝑡
D-Var
Γ⊢𝑛⇒𝖤𝗊𝖨(𝟎,𝑛)
D-Int
𝐴haslength𝑚
Γ⊢𝐴⇒𝖠𝗋𝗋𝖺𝗒𝑚
D-Array
Γ⊢𝑥:𝑠→𝑡𝗍𝗒𝗉𝖾Γ,𝑥:𝑠⊢𝑒⇐𝑡
Γ⊢(𝜆𝑥.𝑒:𝑥:𝑠→𝑡)⇒𝑥:𝑠→𝑡
D-Lam
Γ⊢𝑥:𝑠→𝑡𝗍𝗒𝗉𝖾Γ,𝑓:(𝑥:𝑠→𝑡),𝑥:𝑠⊢𝑒⇐𝑡
Γ⊢(𝖿𝗂𝗑𝑓(𝑥).𝑒:𝑥:𝑠→𝑡)⇒𝑥:𝑠→𝑡
D-Fix
Γ⊢𝑎⇒𝑠|𝑠|=𝗂𝗇𝗍
Γ⊢𝑎+𝑘⇒𝑐𝖲𝗁𝗂𝖿𝗍(𝑎,𝑘)
D-Shift
Γ⊢𝑎⇒𝑠|𝑠|=𝖺𝗋𝗋
Γ⊢𝗅𝖾𝗇𝑎⇒𝑐𝖫𝖾𝗇𝗀𝗍𝗁(𝑎)
D-Length
Γ⊢𝑎⇒𝑠|𝑠|=𝖺𝗋𝗋Γ⊢𝑖⇒𝑢|𝑢|=𝗂𝗇𝗍Γ⊧𝖡𝗇𝖽(𝑎,𝑖)
Γ⊢𝗀𝖾𝗍𝑎𝑖⇒𝑐𝗂𝗇𝗍
D-Get
Γ⊢𝑓⇒𝑥:𝑠→𝑡Γ⊢𝑎⇐𝑠
Γ⊢𝑓𝑎⇒𝑐𝑡[𝑎/𝑥]
D-App
Γ⊢𝑎⇒𝑠Γ⊢𝑠<:𝑡
Γ⊢𝑎⇐𝑡
D-Sub
Γ⊢𝑐⇒𝑐𝑠Γ,𝑥:𝑠⊢𝑒⇐𝑡𝑥∉fv(𝑡)
Γ⊢𝗅𝖾𝗍𝑥=𝑐𝗂𝗇𝑒⇐𝑡
D-Let
Γ,𝛿𝖼𝗍𝗑Γ,𝛿⊢𝑒1⇐𝑡Γ,――𝛿⊢𝑒2⇐𝑡
Γ⊢𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2⇐𝑡
D-If
Γ⊢𝑡𝗍𝗒𝗉𝖾
Γ⊢𝖾𝗋𝗋𝗈𝗋⇐𝑡
D-Error
The VC generator has the three partial judgments Γ⊢𝑎⇒𝑡∣C,Γ⊢𝑐⇒𝑐𝑡∣C,Γ⊢𝑒⇐𝑡∣C. Its subtyping translation, with 𝑧 fresh, is 𝖲𝗎𝖻𝖵𝖢(Γ;{𝜈:𝐵∣𝑝},{𝜈:𝐵∣𝑞})={(Γ,𝑧:{𝜈:𝐵∣𝑝};𝑞[𝑧/𝜈])},𝖲𝗎𝖻𝖵𝖢(Γ;(𝑥:𝑠1→𝑠2),(𝑥:𝑡1→𝑡2))=𝖲𝗎𝖻𝖵𝖢(Γ;𝑡1,𝑠1)⊎𝖲𝗎𝖻𝖵𝖢(Γ,𝑥:𝑡1;𝑠2,𝑡2). It fails on a shape mismatch, an ill-formed displayed type, or a failed recursive call. With 𝖠, 𝖢𝗈𝗆𝗉, and 𝖪 denoting the three deterministic partial functions, the atom clauses are 𝖠Γ(𝑥)=(Γ(𝑥),∅),𝖠Γ(𝑛)=(𝖤𝗊𝖨(𝟎,𝑛),∅),𝖠Γ(⟨𝑛0,…,𝑛𝑚−1⟩)=(𝖠𝗋𝗋𝖺𝗒𝑚,∅),𝖠Γ(𝜆𝑥.𝑒:𝑥:𝑠→𝑡)=(𝑥:𝑠→𝑡,C)if𝖪Γ,𝑥:𝑠(𝑒,𝑡)=C,𝖠Γ(𝖿𝗂𝗑𝑓(𝑥).𝑒:𝑥:𝑠→𝑡)=(𝑥:𝑠→𝑡,C)if𝖪Γ,𝑓:(𝑥:𝑠→𝑡),𝑥:𝑠(𝑒,𝑡)=C. Every annotation and entry is also checked for well-formedness. Put BΓ(𝑎,𝑖)={(Γ;𝛿)∣𝛿isaconjunctof𝖡𝗇𝖽(𝑎,𝑖)}. The computation clauses are 𝖢𝗈𝗆𝗉Γ(𝑎+𝑘)=(𝖲𝗁𝗂𝖿𝗍(𝑎,𝑘),C)if𝖠Γ(𝑎)=(𝑠,C)and|𝑠|=𝗂𝗇𝗍,𝖢𝗈𝗆𝗉Γ(𝗅𝖾𝗇𝑎)=(𝖫𝖾𝗇𝗀𝗍𝗁(𝑎),C)if𝖠Γ(𝑎)=(𝑠,C)and|𝑠|=𝖺𝗋𝗋,𝖢𝗈𝗆𝗉Γ(𝗀𝖾𝗍𝑎𝑖)=(𝗂𝗇𝗍,C𝑎⊎C𝑖⊎BΓ(𝑎,𝑖))if𝖠Γ(𝑎)=(𝑠,C𝑎),|𝑠|=𝖺𝗋𝗋,if𝖠Γ(𝑖)=(𝑢,C𝑖),|𝑢|=𝗂𝗇𝗍,𝖢𝗈𝗆𝗉Γ(𝑓𝑎)=(𝑡[𝑎/𝑥],C𝑓⊎C𝑎)if𝖠Γ(𝑓)=(𝑥:𝑠→𝑡,C𝑓)and𝖪Γ(𝑎,𝑠)=C𝑎. The checking clauses, where 𝑒𝛿=𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2, are 𝖪Γ(𝑎,𝑡)=C⊎𝖲𝗎𝖻𝖵𝖢(Γ;𝑠,𝑡)if𝖠Γ(𝑎)=(𝑠,C),𝖪Γ(𝗅𝖾𝗍𝑥=𝑐𝗂𝗇𝑒,𝑡)=C𝑐⊎C𝑒if𝖢𝗈𝗆𝗉Γ(𝑐)=(𝑠,C𝑐),if𝖪Γ,𝑥:𝑠(𝑒,𝑡)=C𝑒,if𝑥∉fv(𝑡),𝖪Γ(𝑒𝛿,𝑡)=C1⊎C2if𝖪Γ,𝛿(𝑒1,𝑡)=C1,if𝖪Γ,――𝛿(𝑒2,𝑡)=C2,𝖪Γ(𝖾𝗋𝗋𝗈𝗋,𝑡)=∅. No other clause is implicit. A generated sequent is accepted only after the certificate checker above accepts its evidence.
For finite-qualifier inference, 𝐾 is a finite set of predicate unknowns and each 𝑄𝜅 is a finite set of well-scoped difference atoms. An assignment 𝜂∈∏𝜅∈𝐾P(𝑄𝜅) replaces 𝜅 by the conjunction of the chosen subset; the empty subset means 𝗍𝗋𝗎𝖾. Enumeration instantiates the annotated program, runs the displayed VC generator, and accepts the first assignment for which every generated VC has checked replay evidence. This is the entire Liquid fragment: it introduces no additional typing or entailment rule.
The first-order dynamic guard is a source term defined by 𝗀𝗎𝖺𝗋𝖽(𝗍𝗋𝗎𝖾,𝑒)=𝑒,𝗀𝗎𝖺𝗋𝖽(𝛿∧𝑝,𝑒)=𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝗀𝗎𝖺𝗋𝖽(𝑝,𝑒)𝖾𝗅𝗌𝖾𝖾𝗋𝗋𝗈𝗋. It is not a higher-order contract wrapper. The source proof-carrying-code package is (𝑒,𝑡,Π). Its consumer parses the chapter grammar and checks well-formedness; recomputes the complete VC list for ⋅⊢𝑒⇐𝑡; matches Π against that recomputed list and replays every certificate; and enables the displayed source reduction only after all checks accept. Producer-supplied VC lists are ignored.
For the erasure boundary, simple types and contexts are 𝜏::=𝗂𝗇𝗍∣𝖺𝗋𝗋∣𝜏→𝜏,Ξ::=⋅∣Ξ,𝑥:𝜏. Erasure maps refinements to their base shape, dependent arrows to simple arrows, declarations pointwise, and guard entries to nothing. It is homomorphic on terms except for erasing lambda and fixpoint refinements from annotations; dynamic conditionals and 𝖾𝗋𝗋𝗈𝗋 remain. The target has the three judgments Ξ⊢0𝑎⇒𝜏, Ξ⊢0𝑐⇒𝑐𝜏, and Ξ⊢0𝑒⇐𝜏. Its complete term table is
𝑥:𝜏∈Ξ
Ξ⊢0𝑥⇒𝜏
ST-Var
Ξ⊢0𝑛⇒𝗂𝗇𝗍
ST-Int
Ξ⊢0𝐴⇒𝖺𝗋𝗋
ST-Array
Ξ,𝑥:𝜏⊢0𝑒⇐𝜎
Ξ⊢0(𝜆𝑥.𝑒:𝜏→𝜎)⇒𝜏→𝜎
ST-Lam
Ξ,𝑓:(𝜏→𝜎),𝑥:𝜏⊢0𝑒⇐𝜎
Ξ⊢0(𝖿𝗂𝗑𝑓(𝑥).𝑒:𝜏→𝜎)⇒𝜏→𝜎
ST-Fix
Ξ⊢0𝑎⇒𝗂𝗇𝗍
Ξ⊢0𝑎+𝑘⇒𝑐𝗂𝗇𝗍
ST-Shift
Ξ⊢0𝑎⇒𝖺𝗋𝗋
Ξ⊢0𝗅𝖾𝗇𝑎⇒𝑐𝗂𝗇𝗍
ST-Length
Ξ⊢0𝑎⇒𝖺𝗋𝗋Ξ⊢0𝑖⇒𝗂𝗇𝗍
Ξ⊢0𝗀𝖾𝗍𝑎𝑖⇒𝑐𝗂𝗇𝗍
ST-Get
Ξ⊢0𝑓⇒𝜏→𝜎Ξ⊢0𝑎⇐𝜏
Ξ⊢0𝑓𝑎⇒𝑐𝜎
ST-App
Ξ⊢0𝑎⇒𝜏
Ξ⊢0𝑎⇐𝜏
ST-Atom
Ξ⊢0𝑐⇒𝑐𝜏Ξ,𝑥:𝜏⊢0𝑒⇐𝜎
Ξ⊢0𝗅𝖾𝗍𝑥=𝑐𝗂𝗇𝑒⇐𝜎
ST-Let
𝛿over𝑉(Ξ)Ξ⊢0𝑒1⇐𝜏Ξ⊢0𝑒2⇐𝜏
Ξ⊢0𝗂𝖿𝛿𝗍𝗁𝖾𝗇𝑒1𝖾𝗅𝗌𝖾𝑒2⇐𝜏
ST-If
Ξ⊢0𝜏𝗍𝗒𝗉𝖾
Ξ⊢0𝖾𝗋𝗋𝗈𝗋⇐𝜏
ST-Error
Simple formation has exactly the three base/arrow constructors of 𝜏; there is no target subtyping judgment. The target deliberately omits the source bounds premise from ST-Get.