Skip to content

Commit 743af00

Browse files
committed
Updated validation rules to match latest semantics of WebAssembly#137
As of commit 275c449 - `rethrow` is as in the first proposal. - Labels do get a new attribute `kind` which is set to `try` or `catch' for labels surrounding instructions which start with `try` or `catch` respectively, and empty otherwise. This is used to validate `delegate` and `rethrow`/`unwind` respectively. - `unwind` can no longer be a target of `rethrow`'s immediate - The `Caught` stack is removed. I also added a file with Wasm code examples from comments (referenced), and what they reduce to according to these semantics. The first example is the only one with a full reduction, and it uses all new instructions, so it's hopefully easy to get an idea of how this works, even for readers without much formal spec involvement.
1 parent 3feb115 commit 743af00

File tree

2 files changed

+274
-36
lines changed

2 files changed

+274
-36
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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".
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. Some times/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 index, then `a_x` denotes its exception tag, i.e., `F_exn(x) = a_x`, where `F` is the current frame.
16+
17+
## example 0
18+
19+
The only example with an almost full reduction trace, and all new instructions (`rethrow` is hidden in `unwind`'s reduct). The first 3 steps, reducing the several `try`s to their respective administrative instructions, are not shown.
20+
21+
```
22+
(func (result i32) (local i32)
23+
try
24+
try
25+
try
26+
throw x
27+
unwind
28+
i32.const 27
29+
local.set 0
30+
end
31+
delegate 0
32+
catch x
33+
local.get 0
34+
end)
35+
```
36+
37+
Take the frame `F = (locals i32.const 0, module m)`. We have:
38+
39+
```
40+
↪ ↪ ↪ F; catch_1{a_x local.get 0} (label_1{}
41+
(delegate{0} (label_0{}
42+
(catch_0{all i32.const 27 local.set 0 rethrow 0} (label_0{} ;; the try-unwind
43+
throw a_x end) end) end) end) end) end
44+
45+
```
46+
47+
For the throw context `T = label_0{}[_]end` the above is the same as
48+
49+
```
50+
F; catch_1{a_x local.get 0} (label_1{}
51+
(delegate{0} (label_0{}
52+
(catch_0{all i32.const 27 local.set 0 rethrow 0}
53+
T[throw a_x] end) end) end) end) end
54+
55+
↪ F; catch_1{a_x local.get 0} (label_1{}
56+
(delegate{0} (label_0{}
57+
(caught{a_x} (label_0{} i32.const 27 local.set 0 rethrow 0
58+
end) end) end) end) end) end
59+
```
60+
61+
Let `F'` be the frame `{locals i32.const 27, module m}`, and let `B^1 = label_0{} [_] end`.
62+
63+
```
64+
↪ F'; catch_1{a_x local.get 0} (label_1{}
65+
(delegate{0} (label_0{}
66+
(caught{a_x} B^1 [rethrow 0] end) end) end) end) end
67+
68+
↪ F'; catch_1{a_x local.get 0} (label_1{}
69+
(delegate{0} (label_0{}
70+
(caught{a_x} B^1 [throw a_x] end) end) end) end) end
71+
```
72+
73+
Let `T' = label_0{} (caught{a_x} B^1 [_] end) end`.
74+
75+
```
76+
↪ F'; catch_1{a_x local.get 0} (label_1{} throw a_x end) end
77+
78+
↪ F'; caught_1{a_x} (label_1{} local.get 0 end) end
79+
80+
↪ ↪ ↪ i32.const 27
81+
```
82+
83+
## behaviour of `rethrow`
84+
85+
### example 1
86+
87+
Interaction of `rethrow` with `unwind`. Taken from [this comment](https://github.com/WebAssembly/exception-handling/issues/87#issuecomment-705586912) by @rossberg.
88+
89+
```
90+
try
91+
throw x
92+
catch x
93+
try
94+
instr1*
95+
rethrow 0
96+
unwind
97+
instr2*
98+
end
99+
end
100+
```
101+
102+
Reduces to
103+
104+
```
105+
caught_0{a_x} (label_0 {} (caught_0{a_x} (label_0 {} instr2* throw a_x end) end) end) end
106+
```
107+
108+
which in turn reduces to `throw a_x`.
109+
110+
Note that any global state changes due to `instr1*` or `instr2*` will take place.
111+
112+
### example 2
113+
114+
`rethrow`'s immediate validation error.
115+
116+
@aheejin gave the following
117+
[example in this comment](https://github.com/WebAssembly/exception-handling/pull/143#discussion_r522673735)
118+
119+
```
120+
try $label0
121+
rethrow $label0 ;; cannot be done, because it's not within catch below
122+
catch
123+
end
124+
```
125+
126+
This is a validation error (no catch block at given rethrow depth).
127+
128+
## target of `delegate`'s immediate (label depth)
129+
130+
@aheejin gave the following
131+
[examples in this comment](https://github.com/WebAssembly/exception-handling/pull/143#discussion_r522673735)
132+
133+
### example 3
134+
135+
`delegate` inside a catch is a validation error.
136+
137+
```
138+
try $label0
139+
catch
140+
try
141+
...
142+
delegate $label0 ;; cannot be done, because $label0's catch is not below but above here
143+
end
144+
```
145+
146+
This is a validation error because `delegate`'s `$label0` refers to the catch-label `label { result ε, type catch}`, not to a try-label.
147+
148+
### example 4
149+
150+
`delegate` correctly targetting a `try-delegate` and a `try-catch`.
151+
152+
```
153+
try $label1
154+
try $label0
155+
try
156+
throw x
157+
delegate $label0
158+
delegate $label1
159+
catch x
160+
instr*
161+
end
162+
```
163+
164+
The thrown exception is (eventually) caught by the outer try's `catch x`, so the above reduces to
165+
166+
```
167+
caught_0{a_x} (label_0 {} instr* end) end
168+
```
169+
170+
171+
## interaction of `delegate` and `unwind`
172+
173+
Two examples from issue #130.
174+
175+
### example 5
176+
177+
The [opening example](https://github.com/WebAssembly/exception-handling/issues/130#issue-713113953)
178+
of issue #130.
179+
180+
```
181+
i32.const 11
182+
global.set 0
183+
try $l
184+
try
185+
try
186+
throw x
187+
delegate 1
188+
unwind
189+
i32.const 27
190+
global.set 0
191+
end
192+
catch_all
193+
end
194+
global.get 0
195+
```
196+
197+
Here, `delegate 1` targets the label `$l` (so it would be the same if we wrote `delegate $l` instead).
198+
199+
This example returns `11`, because `delegate` skips everything up to and not including `try $l`.
200+
201+
### example 6
202+
203+
This example
204+
[appears to keep](https://github.com/WebAssembly/exception-handling/issues/130#issuecomment-704249682)
205+
the issue #130 open.
206+
207+
@RossTate expressed concerns with respect to an example possibly equivalent to
208+
the one below. "Possibly", because the original example in the comment refers to
209+
an `unwinding` branch, first presented in issue #124, so I attempted to rewrite
210+
the example to match the current syntax as best I could.
211+
212+
```
213+
try $t
214+
try $l
215+
try $u
216+
try
217+
throw x
218+
delegate $t
219+
unwind
220+
instr1*
221+
end
222+
catch x
223+
instr2*
224+
end
225+
instr3*
226+
catch_all
227+
instr4*
228+
end
229+
```
230+
231+
The thrown exception tag `a_x` is delegated to the outer `try $l - catch_all`, ignoring the `try $u - unwind` and `try - catch x` in between. So this example reduces to
232+
233+
```
234+
caught_0{a_x} (label_0{} instr4* end) end
235+
```
236+
237+
During the above reduction, `instr1*`, `instr2*`, and `instr3*` are never executed.
238+
239+

proposals/exception-handling/Exceptions-formal-overview.md

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,46 +39,52 @@ mod ::= module ... exn*
3939

4040
## Validation (Typing)
4141

42-
To verify that the `rethrow l` instruction refers to a surrounding catch block, we introduce a stack `caught` to validation contexts, which gets an exception index or the keyword `all` prepended whenever we enter instructions inside a `catch exnidx` or `catch_all` block, respectively. This addition is reflected in the execution rules, by the administrative instruction `caught` which models the stack of caught exceptions on the wasm stack.
4342

43+
To verify that a `try...delegate l` instruction refers to a label surrounding the instructions of a try block (call this a try-label), introduce a `kind` attribute to labels in the validation context, which is set to `try` when the label is a try-label.
4444

45-
### Instructions
45+
Similarly, to verify that the `rethrow l` instruction refers to a label surrounding the instructions of a catch block (call this a catch-label), we allow the `kind` attribute of labels in the validation context to be set to `catch` when the label is a catch-label. This addition is reflected in the execution rules, by the administrative instruction `caught` which introduces a label around the catching try-block.
46+
47+
The original notation `label [t*]` is now a shortcut for `label {result [t*], kind ε}`.
4648

4749

50+
### Instructions
51+
4852

4953
```
5054
C_exn(x) = [t*] -> []
5155
--------------------------------
52-
C |- throw x : [t1* t*] -> [t2*]
56+
C throw x : [t1* t*] -> [t2*]
5357
5458
55-
C_caught(l) =/= ε
59+
C_label(l) =/= ε
60+
C_label(l).kind = catch
5661
-------------------------------
57-
C |- rethrow l : [t1*] -> [t2*]
62+
C rethrow l : [t1*] -> [t2*]
5863
5964
6065
bt = [t1*] -> [t2*]
61-
C, label [t2*] |- instr* : [t1*] -> [t2*]
66+
C, label {result [t2*], kind try} ⊢ instr* : [t1*] -> [t2*]
6267
(C_exn(x_i) = [t'_i*] -> [])^n
63-
(C, label [t2*], caught x_i |- instr_i* : [t'_i*] -> [t2*])^n
64-
(C, label [t2*], caught all |- instr'* : [] -> [t2*])^k
68+
(C, label { result [t2*], kind catch } ⊢ instr_i* : [t'_i*] -> [t2*])^n
69+
(C, label { result [t2*], kind catch } ⊢ instr'* : [] -> [t2*])^k
6570
(k=0 and n>0) or (k=1 and n≥0)
6671
------------------------------------------------------------------
67-
try bt instr* (catch x_i instr_i*)^n (catch_all instr'*)^k end : bt
72+
C ⊢ try bt instr* (catch x_i instr_i*)^n (catch_all instr'*)^k end : bt
6873
6974
7075
bt = [t1*] -> [t2*]
71-
C, label [t2*] |- instr* : [t1*] -> [t2*]
76+
C, label {result [t2*], kind try} ⊢ instr* : [t1*] -> [t2*]
7277
C_label(l) =/= ε
73-
-----------------------------------------
74-
try bt instr* delegate l : bt
78+
C_label(l).kind = try
79+
------------------------------------------------------------
80+
C ⊢ try bt instr* delegate l : bt
7581
7682
7783
bt = [t1*] -> [t2*]
78-
C, label [t2*] |- instr_1* : [t1*] -> [t2*]
79-
C, label [t2*] |- instr_2* : [] -> []
80-
----------------------------------------------------
81-
try bt instr_1* unwind instr_2* end : bt
84+
C, label {result [t2*], kind try} ⊢ instr_1* : [t1*] -> [t2*]
85+
C, label [t2*] instr_2* : [] -> []
86+
--------------------------------------------------------------
87+
C ⊢ try bt instr_1* unwind instr_2* end : bt
8288
```
8389

8490
## Execution (Reduction)
@@ -110,59 +116,52 @@ instr ::= ... | throw a | catch_n{a instr*}*{instr*}? instr* end
110116
| delegate{l} instr* end | caught_m{a val^n} instr* end
111117
```
112118

113-
Caught exceptions stack
114-
115-
```
116-
C^0 ::= val^m [_] instr
117-
C^{n+1} ::= caught{exnaddr val^k} C^n end
118-
```
119-
120119
Throw Contexts
121120

122121
```
123-
T ::= v* [_] e* | label_n{e*} T end | caught_m{a val^n} T end | frame_n{F} T end
122+
T ::= val* [_] instr* | label_n{instr*} T end | caught_m{a val^n} T end | frame_n{F} T end
124123
```
125124

126125
### Instructions
127126

128127

129128
```
130-
F; throw x --> F; throw a (iff F_exn(x) = a)
129+
F; throw x F; throw a (iff F_exn(x) = a)
131130
132-
caught_m{a val^n} C^l[rethrow l] end
133-
--> caught_m{a val^n} C^l[val^n (throw a)] end
131+
caught_m{a val^n} B^{l+1}[rethrow l] end
132+
caught_m{a val^n} B^{l+1}[val^n (throw a)] end
134133
135-
caught_m{a val^n} val^m end --> val^m
134+
caught_m{a val^n} val^m end val^m
136135
```
137136

138137
A keyword `all` is introduced to simplify the requirements for the `try` execution step below.
139138

140139
```
141140
F; val^n (try bt instr* (catch x_i instr_i*)* (catch_all instr'*)? end)
142-
--> F; catch_m{a_i instr_i*}*{all instr'*}? (label_m{} val^n instr* end) end
141+
F; catch_m{a_i instr_i*}*{all instr'*}? (label_m{} val^n instr* end) end
143142
(iff bt = [t1^n] -> [t2^m] and (F_exn(x_i) = a_i)*)
144143
145-
catch_m{a_i instr_i*}*{all instr'*}? val^m end --> val^m
144+
catch_m{a_i instr_i*}*{all instr'*}? val^m end val^m
146145
147146
S; F; catch_m{a_i instr_i*}*{all instr'*}? T[val^n (throw a)] end
148-
--> S; F; caught_m{a val^n} val^n instr_i* end
147+
S; F; caught_m{a val^n} (label_m{} val^n instr_i* end) end
149148
(iff S_exn(a) = {type [t^n]->[]} and i is the least such that a_i = a)
150149
151150
S; F; catch_m{a_i instr_i*}*{all instr*} T[val^n (throw a)] end
152-
--> S; F; caught_m{a val^n} instr* end
151+
S; F; caught_m{a val^n} (label_m instr* end) end
153152
(iff S_exn(a) = {type [t^n]->[]} and for every i, a_i =/= a)
154153
155154
S; F; catch_m{a_i instr_i*}* T[val^n (throw a)] end
156-
--> S; F; val^n (throw a)
155+
S; F; val^n (throw a)
157156
(iff for every i, a_i =/= a)
158157
159158
160-
val^n (try bt instr* delegate l) --> delegate{l} (label_m{} val^n instr* end) end
159+
val^n (try bt instr* delegate l) delegate{l} (label_m{} val^n instr* end) end
161160
(iff bt = [t^n] -> [t^m])
162161
163162
B^l[ delegate{l} (T[val^n (throw a)]) end ] end
164-
--> val^n (throw a)
163+
val^n (throw a)
165164
166165
167-
try bt instr* unwind instr'* end --> try bt instr* catch_all instr'* rethrow 0 end
166+
try bt instr* unwind instr'* end try bt instr* catch_all instr'* rethrow 0 end
168167
```

0 commit comments

Comments
 (0)