Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 3dc715a

Browse files
ioannadrossbergaheejinRossTate
authored
formal spec overview for the 3rd exception handling proposal (#143)
* formal spec overview for the 3rd exception handling proposal This adds a condensed version of the current core formal spec, and some examples of reductions according to this spec. These files were added to aid in discussions when finalising spec details. After 260 PR comments and suggestions co-authors are: Co-authored-by: Andreas Rossberg <[email protected]> Co-authored-by: Heejin Ahn <[email protected]> Co-authored-by: Ross Tate <[email protected]>
1 parent 539be03 commit 3dc715a

File tree

4 files changed

+621
-7
lines changed

4 files changed

+621
-7
lines changed

interpreter/exec/eval.ml

+1-5
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ let rec step (c : config) : config =
236236
let n1 = Lib.List32.length ts1 in
237237
let n2 = Lib.List32.length ts2 in
238238
let args, vs' = take n1 vs e.at, drop n1 vs e.at in
239-
let k = Int32.succ x.it in
240-
vs', [Label (n2, [], ([], [Delegate (k, (args, List.map plain es')) @@ e.at])) @@ e.at]
239+
vs', [Label (n2, [], ([], [Delegate (x.it, (args, List.map plain es')) @@ e.at])) @@ e.at]
241240

242241
| Drop, v :: vs' ->
243242
vs', []
@@ -693,9 +692,6 @@ let rec step (c : config) : config =
693692
| Catch (n, cts, ca, (vs', [])), vs ->
694693
vs' @ vs, []
695694

696-
| Catch (n, cts, ca, (vs', {it = Delegating (0l, a, vs0); at} :: es')), vs ->
697-
vs, [Catch (n, cts, ca, (vs', (Throwing (a, vs0) @@ at) :: es')) @@ e.at]
698-
699695
| Catch (n, cts, ca, (vs', ({it = Trapping _ | Breaking _ | Returning _ | Delegating _; at} as e) :: es')), vs ->
700696
vs, [e]
701697

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
# 3rd Proposal Formal Spec Examples
2+
3+
This document contains WebAssembly code examples mentioned in comments on this repository, and what they reduce to, according to the ["3rd proposal formal spec overview"](Exceptions-formal-overview.md).
4+
5+
Its purpose is to make sure everyone is happy with the implications of the semantics in the current 3rd proposal, or to aid discussions on these semantics.
6+
7+
The first *example 0* contains all the new instructions, and it is the only one with an almost full reduction displayed. It is meant to easily show how the spec works, even if the reader has not spent much time with the WebAssembly formal spec.
8+
9+
For all other examples just the result of the reduction is given. These examples are taken from comments in this repository, which are linked. Sometimes/often the examples are modified to fit the current syntax.
10+
11+
If anyone would like that I add another reduction trace, or other examples, please let me know, I'd be happy to.
12+
13+
### Notation
14+
15+
If `x` is an exception tag index, then `a_x` denotes its exception tag address, i.e., `a_x := F.tag[x]`, where `F` is the current frame.
16+
17+
Note that the block contexts and throw contexts given for the reductions are the largest possible in each case, so the reduction steps are the only possible ones.
18+
19+
## Example 0
20+
21+
The only example with an almost full reduction trace, and all new instructions. Such explicit reduction steps are only shown in Example 4 and Example 5, to highlight the reduction step of the administrative `delegate`.
22+
23+
In the following reduction, we don't show the first 4 steps, which just reduce the several `try`s and the `throw x` to their respective administrative instructions. The type of the tag `$x` here is `[]→[]`.
24+
25+
```
26+
(tag $x)
27+
(func (result i32) (local i32)
28+
try (result i32)
29+
try
30+
try
31+
throw $x
32+
catch_all
33+
i32.const 27
34+
local.set 0
35+
rethrow 0
36+
end
37+
delegate 0
38+
catch $x
39+
local.get 0
40+
end)
41+
```
42+
43+
We write the body of this function in folded form, because it is easier to parse.
44+
45+
```
46+
(try (result i32)
47+
(do
48+
(try
49+
(do
50+
(try
51+
(do
52+
(throw $x))
53+
(catch_all
54+
(local.set 0 (i32.const 27))
55+
(rethrow 0))))
56+
(delegate 0)))
57+
(catch $x
58+
(local.get 0)))
59+
```
60+
61+
Take the frame `F = (locals i32.const 0, module m)`. We have:
62+
63+
```
64+
↪ ↪ ↪
65+
↪ F; (label_1{}
66+
(catch_1{ a_x (local.get 0) }
67+
(label_0{}
68+
(delegate{ 0 }
69+
(label_0{}
70+
(catch_0{ ε (local.set 0 (i32.const 27)) (rethrow 0) }
71+
(throw a_x) end) end) end) end) end) end)
72+
```
73+
74+
For the trivial throw context `T = [_]` the above is the same as
75+
76+
```
77+
↪ F; (label_1{}
78+
(catch_1{ a_x (local.get 0) }
79+
(label_0{}
80+
(delegate{ 0 }
81+
(label_0{}
82+
(catch_0{ ε (local.set 0 (i32.const 27)) (rethrow 0) }
83+
T[(throw a_x)]) end) end) end) end) end)
84+
85+
↪ F; (label_1{}
86+
(catch_1{ a_x (local.get 0) }
87+
(label_0{}
88+
(delegate{ 0 }
89+
(label_0{}
90+
(caught_0{ a_x ε }
91+
(local.set 0 (i32.const 27))
92+
(rethrow 0) end) end) end) end) end) end)
93+
```
94+
95+
Let `F'` be the frame `{locals i32.const 27, module m}`, and let `B^0 = [_]` to reduce `rethrow 0`.
96+
97+
```
98+
↪ F'; (label_1{}
99+
(catch_1{ a_x (local.get 0) }
100+
(label_0{}
101+
(delegate{ 0 }
102+
(label_0{}
103+
(caught_0{ a_x ε }
104+
B^0[ (rethrow 0) ] end) end) end) end) end) end)
105+
106+
↪ F'; (label_1{}
107+
(catch_1{ a_x (local.get 0) }
108+
(label_0{}
109+
(delegate{ 0 }
110+
(label_0{}
111+
(caught_0{ a_x ε }
112+
(throw a_x) end) end) end) end) end) end)
113+
```
114+
115+
Let `T' = (label_0{} (caught_0{ a_x ε } [_] end) end)` and use the same `B^0` as above to reduce the throw with the delegate.
116+
117+
```
118+
↪ F'; (label_1{}
119+
(catch_1{ a_x (local.get 0) }
120+
(label_0{}
121+
B^0[ (delegate{ 0 } T'[ (throw a_x) ] end) ] end) end) end)
122+
123+
↪ F'; (label_1{}
124+
(catch_1{ a_x (local.get 0) }
125+
(throw a_x) end) end)
126+
```
127+
128+
Use the trivial throw context `T` again, this time to match the throw to the `catch_1`.
129+
130+
```
131+
↪ F'; (label_1{}
132+
(catch_1{ a_x (local.get 0) }
133+
T[ (throw a_x) ] end) end)
134+
135+
↪ F'; (label_1{}
136+
(caught_1{ a_x ε }
137+
(local.get 0) end) end)
138+
139+
↪ F'; (label_1{}
140+
(caught_1{ a_x ε }
141+
(i32.const 27) end) end)
142+
143+
↪ F'; (label_1{}
144+
(i32.const 27) end)
145+
146+
↪ F'; (i32.const 27)
147+
```
148+
149+
## Behavior of `rethrow`
150+
151+
### Example 1
152+
153+
Location of a rethrown exception. Let `x, y, z` be tag indices of tags with type `[t_x]→[]`, `[t_y]→[]`, and `[t_z]→[]` respectively. Let `val_p : t_p` for every `p` among `x, y, z`.
154+
155+
```
156+
try
157+
val_x
158+
throw x
159+
catch x
160+
try $label2
161+
val_y
162+
throw y
163+
catch_all
164+
try
165+
val_z
166+
throw z
167+
catch z
168+
rethrow $label2 ;; This is rethrow 2.
169+
end
170+
end
171+
end
172+
```
173+
174+
Folded it looks as follows.
175+
176+
```
177+
(try
178+
(do
179+
val_x
180+
(throw x))
181+
(catch x ;; <--- (rethrow 2) targets this catch.
182+
(try
183+
(do
184+
val_y
185+
(throw y))
186+
(catch_all
187+
(try
188+
(do
189+
val_z
190+
(throw z))
191+
(catch z
192+
(rethrow 2)))))))
193+
```
194+
195+
In the above example, all thrown exceptions get caught and the first one gets rethrown from the catching block of the last one. So the above reduces to
196+
197+
```
198+
(label_0{}
199+
(caught_0{ a_x val_x } ;; <---- The exception rethrown by `rethrow 2` below.
200+
val_x
201+
(label_0{}
202+
(caught_0{ a_y val_y }
203+
;; The catch_all does not leave val_y here.
204+
(label_0{}
205+
(caught_0{ a_z val_z }
206+
val_z
207+
;; (rethrow 2) puts val_x and the throw below.
208+
val_x
209+
(throw a_x) end) end) end) end) end) end)
210+
```
211+
212+
This reduces to `val_x (throw a_x)`, throwing to the caller.
213+
214+
### Example 2
215+
216+
`rethrow`'s immediate validation error.
217+
218+
@aheejin gave the following
219+
[example in this comment](https://github.com/WebAssembly/exception-handling/pull/143#discussion_r522673735)
220+
221+
```
222+
(func
223+
try $label0
224+
rethrow $label0 ;; cannot be done, because it's not within catch below
225+
catch x
226+
end)
227+
```
228+
229+
This is a validation error (no catch block at given rethrow depth).
230+
231+
## Target of `delegate`'s Immediate (Label Depth)
232+
233+
### Example 3
234+
235+
`delegate 0` target.
236+
237+
```
238+
(try $l
239+
(do
240+
(throw x))
241+
(delegate $l))
242+
```
243+
244+
This is a validation error, a `delegate` always refers to an outer block.
245+
246+
### Example 4
247+
248+
`delegate` correctly targeting a `try-delegate` and a `try-catch`.
249+
250+
```
251+
try $label1
252+
try
253+
try $label0
254+
try
255+
throw x
256+
delegate $label0 ;; delegate 0
257+
delegate $label1 ;; delegate 1
258+
catch_all
259+
end
260+
catch x
261+
instr*
262+
end
263+
```
264+
265+
In folded form and reduced to the point `throw x` is called, this is:
266+
267+
```
268+
(label_0{}
269+
(catch_0{ a_x instr* }
270+
(label_0{}
271+
(catch_0{ ε ε }
272+
(label_0{}
273+
(delegate{ 1 }
274+
(label_0{}
275+
(delegate{ 0 }
276+
(throw a_x) end) end) end) end) end) end) end) end)
277+
```
278+
279+
The `delegate{ 0 }` reduces using the trivial throw and block contexts to:
280+
281+
```
282+
(label_0{}
283+
(catch_0{ a_x instr* }
284+
(label_0{}
285+
(catch_0{ ε ε }
286+
(label_0{}
287+
(delegate{ 1 }
288+
(throw a_x) end) end) end) end) end) end)
289+
```
290+
291+
The `delegate{ 1 }` reduces using the trivial throw context and the block context `B^1 := (catch_0{ ε ε } (label_0{} [_] end) end)` to the following:
292+
293+
```
294+
(label_0{}
295+
(catch_0{ a_x instr* }
296+
(throw a_x) end) end)
297+
```
298+
The thrown exception is (eventually) caught by the outer try's `catch x`, so the above reduces to the following.
299+
300+
```
301+
(label_0 {}
302+
(caught_0{a_x}
303+
instr* end) end)
304+
```
305+
306+
### Example 5
307+
308+
`delegate 0` targeting a catch inside a try.
309+
310+
```
311+
try (result i32)
312+
try $label0
313+
throw x
314+
catch_all
315+
try
316+
throw y
317+
delegate $label0 ;; delegate 0
318+
end
319+
catch_all
320+
i32.const 4
321+
end
322+
```
323+
324+
In folded form this is:
325+
326+
```
327+
(try (result i32)
328+
(do
329+
(try
330+
(do
331+
(throw x))
332+
(catch_all
333+
(try
334+
(do
335+
(throw y)
336+
(delegate 0))))))
337+
(catch_all
338+
(i32.const 4)))
339+
```
340+
341+
When it's time to reduce `(throw y)`, the reduction looks as follows.
342+
343+
```
344+
(label_1{}
345+
(catch_1{ ε (i32.const 4) }
346+
(label_0{}
347+
(caught_0{ a_x ε }
348+
(label_0{}
349+
(delegate{ 0 }
350+
(throw a_y) end) end) end) end) end) end)
351+
```
352+
353+
For `B^0 := [_] := T`, the above is the same as the following.
354+
355+
```
356+
(label_1{}
357+
(catch_1{ ε (i32.const 4) }
358+
(label_0{}
359+
(caught_0{ a_x ε }
360+
(label_0{}
361+
B^0 [(delegate{ 0 } T[ (throw a_y) ] end)] end) end) end) end) end)
362+
363+
↪ (label_1{}
364+
(catch_1{ ε (i32.const 4) }
365+
(label_0{}
366+
(caught_0{ a_x ε }
367+
(throw a_y) end) end) end) end)
368+
```
369+
370+
So `throw a_y` gets correctly caught by `catch_1{ ε (i32.const 4) }` and this example reduces to `(i32.const 4)`.

0 commit comments

Comments
 (0)