-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCapaSyntax.agda
More file actions
363 lines (294 loc) · 11.4 KB
/
Copy pathCapaSyntax.agda
File metadata and controls
363 lines (294 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
{-# OPTIONS --safe #-}
------------------------------------------------------------------
-- CapaSyntax.agda
--
-- Syntax, typing relation, and small-step reduction for the
-- lambda_cap calculus described in docs/semantics.md. Includes
-- PLFA-style parallel renaming / substitution (used by R-Beta
-- and by the preservation proof in CapaSoundness.agda).
--
-- STATUS: Typechecks on Agda >= 2.6.4. Verified in CI (see
-- .github/workflows/agda.yml).
--
-- The conventions follow Programming Language Foundations in Agda
-- (PLFA), Wadler/Kokke/Siek, which is the canonical reference for
-- this Wright-Felleisen style.
------------------------------------------------------------------
module CapaSyntax where
------------------------------------------------------------------
-- Basic library types we depend on.
------------------------------------------------------------------
data Nat : Set where
zero : Nat
suc : Nat -> Nat
data Bool : Set where
true : Bool
false : Bool
_||_ : Bool -> Bool -> Bool
true || _ = true
false || b = b
infixr 5 _||_
data _==_ {A : Set} (x : A) : A -> Set where
refl : x == x
infix 4 _==_
{-# BUILTIN EQUALITY _==_ #-}
------------------------------------------------------------------
-- Capabilities.
--
-- The capability tags are an enumerated set. The constructors
-- match the built-in capabilities Capa ships (capa/runtime/
-- _capabilities.py). User-defined capabilities are handled in
-- the full language but not modelled in lambda_cap.
------------------------------------------------------------------
data Cap : Set where
Stdio : Cap
Fs : Cap
Net : Cap
Env : Cap
Clock : Cap
Random : Cap
Unsafe : Cap
------------------------------------------------------------------
-- Types.
--
-- The types of lambda_cap: base types (Int, Bool, Unit), function
-- types, and capability types (one per Cap tag).
------------------------------------------------------------------
data Ty : Set where
TyInt : Ty
TyBool : Ty
TyUnit : Ty
_=>_ : Ty -> Ty -> Ty -- function
TyCap : Cap -> Ty -- capability type
infixr 7 _=>_
------------------------------------------------------------------
-- Variables (de Bruijn indices).
------------------------------------------------------------------
data Var : Set where
vzero : Var
vsuc : Var -> Var
------------------------------------------------------------------
-- Terms.
--
-- Variables, lambdas, applications, integer literals, the unit
-- value, capability constants (introduced at main), method calls
-- on capabilities (`use`), restrict_to-style attenuation
-- (`restrict`), and the consume-style move (`consume`).
------------------------------------------------------------------
data Tm : Set where
var : Var -> Tm
lam : Ty -> Tm -> Tm
app : Tm -> Tm -> Tm
i : Nat -> Tm -- integer literal
unit : Tm
cap : Cap -> Tm -- capability constant
use : Cap -> Tm -> Tm -- exercise a cap
restrict : Cap -> Tm -> Tm -- attenuate a cap
consume : Tm -> Tm -- linear move
------------------------------------------------------------------
-- Renamings and substitutions (PLFA-style parallel).
--
-- A renaming is a function Var -> Var; a substitution is a
-- function Var -> Tm. `ext` lifts a renaming under a binder
-- (the new vzero stays put, every existing index shifts up by
-- one via the underlying renaming); `exts` does the same for a
-- substitution but the existing indices are first renamed via
-- `rename vsuc` to skip the new binding. `rename` and `subst`
-- then recurse over a term applying the lifted renaming or
-- substitution at each occurrence.
--
-- Termination: `rename` and `subst` are structurally recursive
-- on the Tm argument, so Agda accepts them without pragma.
-- There is no mutual recursion: `ext` -> `rename` -> `exts` ->
-- `subst`.
------------------------------------------------------------------
ext : (Var -> Var) -> (Var -> Var)
ext rho vzero = vzero
ext rho (vsuc x) = vsuc (rho x)
rename : (Var -> Var) -> Tm -> Tm
rename rho (var x) = var (rho x)
rename rho (lam A t) = lam A (rename (ext rho) t)
rename rho (app t1 t2) = app (rename rho t1) (rename rho t2)
rename rho (i n) = i n
rename rho unit = unit
rename rho (cap c) = cap c
rename rho (use c t) = use c (rename rho t)
rename rho (restrict c t) = restrict c (rename rho t)
rename rho (consume t) = consume (rename rho t)
exts : (Var -> Tm) -> (Var -> Tm)
exts sigma vzero = var vzero
exts sigma (vsuc x) = rename vsuc (sigma x)
subst : (Var -> Tm) -> Tm -> Tm
subst sigma (var x) = sigma x
subst sigma (lam A t) = lam A (subst (exts sigma) t)
subst sigma (app t1 t2) = app (subst sigma t1) (subst sigma t2)
subst sigma (i n) = i n
subst sigma unit = unit
subst sigma (cap c) = cap c
subst sigma (use c t) = use c (subst sigma t)
subst sigma (restrict c t) = restrict c (subst sigma t)
subst sigma (consume t) = consume (subst sigma t)
-- Single-variable substitution: `t [ v ]` replaces the zero
-- index in t with v, leaving every other index unshifted.
sub-zero : Tm -> (Var -> Tm)
sub-zero v vzero = v
sub-zero v (vsuc x) = var x
_[_] : Tm -> Tm -> Tm
t [ v ] = subst (sub-zero v) t
infix 6 _[_]
------------------------------------------------------------------
-- Typing contexts.
--
-- A context is a finite list of types. We use the standard
-- snoc-list representation: empty, or (ctx , T).
------------------------------------------------------------------
data Ctx : Set where
empty : Ctx
_,_ : Ctx -> Ty -> Ctx
infixl 5 _,_
------------------------------------------------------------------
-- Variable lookup judgment.
--
-- Index x in context G has type A. de Bruijn-style.
------------------------------------------------------------------
data _>>_!_ : Ctx -> Var -> Ty -> Set where
here : forall {G A} -> ((G , A)) >> vzero ! A
there : forall {G A B v} -> G >> v ! A -> ((G , B)) >> (vsuc v) ! A
infix 4 _>>_!_
------------------------------------------------------------------
-- Capability footprint: the set of capabilities a typing context
-- makes available. Represented as a Cap -> Bool characteristic
-- function.
------------------------------------------------------------------
CapSet : Set
CapSet = Cap -> Bool
emptyCS : CapSet
emptyCS _ = false
singletonCS : Cap -> CapSet
singletonCS Stdio Stdio = true
singletonCS Fs Fs = true
singletonCS Net Net = true
singletonCS Env Env = true
singletonCS Clock Clock = true
singletonCS Random Random = true
singletonCS Unsafe Unsafe = true
singletonCS _ _ = false
_∪_ : CapSet -> CapSet -> CapSet
(S ∪ T) c = S c || T c
infixr 5 _∪_
------------------------------------------------------------------
-- Typing relation.
--
-- G |- t : T meaning "in context G, term t has type T". The
-- capability rules are:
--
-- - cap c has type TyCap c, unconditionally (introduction
-- at main is the only way to obtain one in practice;
-- in the calculus we admit it as a literal).
-- - use c t requires t : TyCap c and produces TyUnit.
-- - restrict c t requires t : TyCap c and produces TyCap c.
-- - consume t reads t at any type and produces the same type;
-- the linear-tracking happens at the operational
-- semantics layer, not in the typing rules.
------------------------------------------------------------------
data _|-_!_ : Ctx -> Tm -> Ty -> Set where
T-Var : forall {G v A}
-> G >> v ! A
-> G |- var v ! A
T-Lam : forall {G A B t}
-> ((G , A)) |- t ! B
-> G |- lam A t ! (A => B)
T-App : forall {G t1 t2 A B}
-> G |- t1 ! (A => B)
-> G |- t2 ! A
-> G |- app t1 t2 ! B
T-Int : forall {G n}
-> G |- i n ! TyInt
T-Unit : forall {G}
-> G |- unit ! TyUnit
T-Cap : forall {G c}
-> G |- cap c ! TyCap c
T-Use : forall {G c t}
-> G |- t ! TyCap c
-> G |- use c t ! TyUnit
T-Restrict : forall {G c t}
-> G |- t ! TyCap c
-> G |- restrict c t ! TyCap c
T-Consume : forall {G t A}
-> G |- t ! A
-> G |- consume t ! A
infix 4 _|-_!_
------------------------------------------------------------------
-- Values.
--
-- Lambdas, integer literals, unit, and capability constants are
-- values. `use`, `app`, `restrict`, and `consume` reduce further.
------------------------------------------------------------------
data Value : Tm -> Set where
V-Lam : forall {A t} -> Value (lam A t)
V-Int : forall {n} -> Value (i n)
V-Unit : Value unit
V-Cap : forall {c} -> Value (cap c)
------------------------------------------------------------------
-- Small-step reduction.
--
-- Standard call-by-value beta. The capability-specific rules:
--
-- - R-Use: use c (cap c) -> unit
-- (exercising a capability returns unit, the runtime
-- effect is modelled by the labelled-transition system
-- in CapaSoundness.agda)
-- - R-Restrict: restrict c (cap c) -> cap c
-- (attenuation produces a capability of the same tag;
-- the narrowing is tracked in the trace, not in the
-- syntax)
-- - R-Consume: consume v -> v when Value v
-- (linear move; the operational invariant is that the
-- original binding is no longer accessible, which the
-- full language enforces via the consume keyword and
-- the analyzer's use-after-consume bookkeeping)
------------------------------------------------------------------
data _==>_ : Tm -> Tm -> Set where
R-AppLeft : forall {t1 t1' t2}
-> t1 ==> t1'
-> app t1 t2 ==> app t1' t2
R-AppRight : forall {v t2 t2'}
-> Value v
-> t2 ==> t2'
-> app v t2 ==> app v t2'
R-Beta : forall {A t v}
-> Value v
-> app (lam A t) v ==> t [ v ]
R-Use : forall {c}
-> use c (cap c) ==> unit
R-UseStep : forall {c t t'}
-> t ==> t'
-> use c t ==> use c t'
R-Restrict : forall {c}
-> restrict c (cap c) ==> cap c
R-RestrictStep : forall {c t t'}
-> t ==> t'
-> restrict c t ==> restrict c t'
R-Consume : forall {v}
-> Value v
-> consume v ==> v
R-ConsumeStep : forall {t t'}
-> t ==> t'
-> consume t ==> consume t'
infix 4 _==>_
------------------------------------------------------------------
-- Reflexive-transitive closure of small-step reduction. Read
-- t ==>* t' as "t reduces to t' in zero or more steps". Used by
-- the Manifest Completeness theorem in CapaSoundness.agda to
-- bound the capabilities reachable via any number of reduction
-- steps.
------------------------------------------------------------------
data _==>*_ : Tm -> Tm -> Set where
done* : forall {t} -> t ==>* t
step* : forall {t t' t''} -> t ==> t' -> t' ==>* t'' -> t ==>* t''
infix 4 _==>*_
------------------------------------------------------------------
-- That is the syntax. The four theorems (Progress + Preservation,
-- composing into Capability Soundness and Manifest Completeness)
-- are stated and proved in CapaSoundness.agda.
------------------------------------------------------------------