Skip to content

Commit 2ccd487

Browse files
authored
[spec/interpreter/test] Reintroduce bottom type (#116)
1 parent 4495265 commit 2ccd487

File tree

8 files changed

+91
-39
lines changed

8 files changed

+91
-39
lines changed

Diff for: document/core/appendix/algorithm.rst

+11-8
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ Types are representable as an enumeration.
2727
2828
type val_type = I32 | I64 | F32 | F64 | Funcref | Externref
2929
30-
func is_num(t : val_type) : bool =
31-
return t = I32 || t = I64 || t = F32 || t = F64
30+
func is_num(t : val_type | Unknown) : bool =
31+
return t = I32 || t = I64 || t = F32 || t = F64 || t = Unknown
3232
33-
func is_ref(t : val_type) : bool =
34-
return t = Funcref || t = Externref
33+
func is_ref(t : val_type | Unknown) : bool =
34+
return t = Funcref || t = Externref || t = Unknown
3535
3636
The algorithm uses two separate stacks: the *value stack* and the *control stack*.
3737
The former tracks the :ref:`types <syntax-valtype>` of operand values on the :ref:`stack <stack>`,
@@ -52,7 +52,6 @@ the latter surrounding :ref:`structured control instructions <syntax-instr-contr
5252
5353
For each value, the value stack records its :ref:`value type <syntax-valtype>`, or :code:`Unknown` when the type is not known.
5454

55-
5655
For each entered block, the control stack records a *control frame* with the originating opcode, the types on the top of the operand stack at the start and end of the block (used to check its result as well as branches), the height of the operand stack at the start of the block (used to check that operands do not underflow the current block), and a flag recording whether the remainder of the block is unreachable (used to handle :ref:`stack-polymorphic <polymorphism>` typing after branches).
5756

5857
For the purpose of presenting the algorithm, the operand and control stacks are simply maintained as global variables:
@@ -222,13 +221,17 @@ Other instructions are checked in a similar manner.
222221
      push_vals(label_types(ctrls[n]))
223222
224223
   case (br_table n* m)
224+
pop_val(I32)
225225
      error_if(ctrls.size() < m)
226+
let arity = label_types(ctrls[m]).size()
226227
      foreach (n in n*)
227-
        error_if(ctrls.size() < n || label_types(ctrls[n]) =/= label_types(ctrls[m]))
228-
pop_val(I32)
229-
      pop_vals(label_types(ctrls[m]))
228+
        error_if(ctrls.size() < n)
229+
        error_if(label_types(ctrls[n]).size() =/= arity)
230+
push_vals(pop_vals(label_types(ctrls[n])))
231+
pop_vals(label_types(ctrls[m]))
230232
      unreachable()
231233
234+
232235
.. note::
233236
It is an invariant under the current WebAssembly instruction set that an operand of :code:`Unknown` type is never duplicated on the stack.
234237
This would change if the language were extended with stack instructions like :code:`dup`.

Diff for: document/core/appendix/index-rules.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ Construct Judgement
2020
:ref:`Memory type <valid-memtype>` :math:`\vdashmemtype \memtype \ok`
2121
:ref:`Global type <valid-globaltype>` :math:`\vdashglobaltype \globaltype \ok`
2222
:ref:`External type <valid-externtype>` :math:`\vdashexterntype \externtype \ok`
23-
:ref:`Instruction <valid-instr>` :math:`S;C \vdashinstr \instr : \functype`
24-
:ref:`Instruction sequence <valid-instr-seq>` :math:`S;C \vdashinstrseq \instr^\ast : \functype`
23+
:ref:`Instruction <valid-instr>` :math:`S;C \vdashinstr \instr : \stacktype`
24+
:ref:`Instruction sequence <valid-instr-seq>` :math:`S;C \vdashinstrseq \instr^\ast : \stacktype`
2525
:ref:`Expression <valid-expr>` :math:`C \vdashexpr \expr : \resulttype`
2626
:ref:`Function <valid-func>` :math:`C \vdashfunc \func : \functype`
2727
:ref:`Table <valid-table>` :math:`C \vdashtable \table : \tabletype`

Diff for: document/core/util/macros.def

+3
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@
209209

210210
.. |externtype| mathdef:: \xref{syntax/types}{syntax-externtype}{\X{externtype}}
211211

212+
.. |stacktype| mathdef:: \xref{syntax/types}{syntax-stacktype}{\X{stacktype}}
213+
.. |opdtype| mathdef:: \xref{syntax/types}{syntax-opdtype}{\X{opdtype}}
214+
212215

213216
.. Types, meta functions
214217

Diff for: document/core/valid/instructions.rst

+44-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
11
.. index:: instruction, function type, context, value, operand stack, ! polymorphism
22
.. _valid-instr:
3+
.. _syntax-stacktype:
4+
.. _syntax-opdtype:
35

46
Instructions
57
------------
68

7-
:ref:`Instructions <syntax-instr>` are classified by :ref:`function types <syntax-functype>` :math:`[t_1^\ast] \to [t_2^\ast]`
8-
that describe how they manipulate the :ref:`operand stack <stack>`.
9-
The types describe the required input stack with argument values of types :math:`t_1^\ast` that an instruction pops off
9+
:ref:`Instructions <syntax-instr>` are classified by *stack types* :math:`[t_1^\ast] \to [t_2^\ast]` that describe how instructions manipulate the :ref:`operand stack <stack>`.
10+
11+
.. math::
12+
\begin{array}{llll}
13+
\production{stack type} & \stacktype &::=&
14+
[\opdtype^\ast] \to [\opdtype^\ast] \\
15+
\production{operand type} & \opdtype &::=&
16+
\valtype ~|~ \bot \\
17+
\end{array}
18+
19+
The types describe the required input stack with *operand types* :math:`t_1^\ast` that an instruction pops off
1020
and the provided output stack with result values of types :math:`t_2^\ast` that it pushes back.
21+
Stack types are akin to :ref:`function types <syntax-functype>`,
22+
except that they allow individual operands to be classified as :math:`\bot`, indicating that the type is unconstrained.
23+
As an auxiliary notion, an operand type :math:`t_1` *matches* another operand type :math:`t_2`, if :math:`t_1` is either :math:`\bot` or equal to :math:`t_2`.
24+
25+
.. _match-opdtype:
26+
27+
.. math::
28+
\frac{
29+
}{
30+
\vdash t \leq t
31+
}
32+
\qquad
33+
\frac{
34+
}{
35+
\vdash \bot \leq t
36+
}
1137
1238
.. note::
1339
For example, the instruction :math:`\I32.\ADD` has type :math:`[\I32~\I32] \to [\I32]`,
@@ -254,7 +280,7 @@ Parametric Instructions
254280

255281
* Else:
256282

257-
* The instruction is valid with type :math:`[t~t~\I32] \to [t]`, for any :ref:`number type <syntax-numtype>` :math:`t`.
283+
* The instruction is valid with type :math:`[t~t~\I32] \to [t]`, for any :ref:`operand type <syntax-opdtype>` :math:`t` that :ref:`matches <match-opdtype>` some :ref:`number type <syntax-numtype>`.
258284

259285
.. math::
260286
\frac{
@@ -263,7 +289,7 @@ Parametric Instructions
263289
}
264290
\qquad
265291
\frac{
266-
t = \numtype
292+
\vdash t \leq \numtype
267293
}{
268294
C \vdashinstr \SELECT : [t~t~\I32] \to [t]
269295
}
@@ -1033,7 +1059,7 @@ Empty Instruction Sequence: :math:`\epsilon`
10331059
............................................
10341060

10351061
* The empty instruction sequence is valid with type :math:`[t^\ast] \to [t^\ast]`,
1036-
for any sequence of :ref:`value types <syntax-valtype>` :math:`t^\ast`.
1062+
for any sequence of :ref:`operand types <syntax-opdtype>` :math:`t^\ast`.
10371063

10381064
.. math::
10391065
\frac{
@@ -1052,13 +1078,17 @@ Non-empty Instruction Sequence: :math:`\instr^\ast~\instr_N`
10521078
for some sequences of :ref:`value types <syntax-valtype>` :math:`t^\ast` and :math:`t_3^\ast`.
10531079

10541080
* There must be a sequence of :ref:`value types <syntax-valtype>` :math:`t_0^\ast`,
1055-
such that :math:`t_2^\ast = t_0^\ast~t^\ast`.
1081+
such that :math:`t_2^\ast = t_0^\ast~{t'}^\ast` where the type sequence :math:`{t'}^\ast` is as long as :math:`t^\ast`.
1082+
1083+
* For each :ref:`operand type <syntax-opdtype>` :math:`t'_i` in :math:`{t'}^\ast` and corresponding type :math:`t_i` in :math:`t^\ast`, :math:`t'_i` :ref:`matches <match-opdtype>` :math:`t_i`.
10561084

10571085
* Then the combined instruction sequence is valid with type :math:`[t_1^\ast] \to [t_0^\ast~t_3^\ast]`.
10581086

10591087
.. math::
10601088
\frac{
1061-
C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~t^\ast]
1089+
C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~{t'}^\ast]
1090+
\qquad
1091+
(\vdash t' \leq t)^\ast
10621092
\qquad
10631093
C \vdashinstr \instr_N : [t^\ast] \to [t_3^\ast]
10641094
}{
@@ -1081,14 +1111,17 @@ Expressions :math:`\expr` are classified by :ref:`result types <syntax-resulttyp
10811111
:math:`\instr^\ast~\END`
10821112
........................
10831113

1084-
* The instruction sequence :math:`\instr^\ast` must be :ref:`valid <valid-instr-seq>` with type :math:`[] \to [t^\ast]`,
1085-
for some :ref:`result type <syntax-resulttype>` :math:`[t^\ast]`.
1114+
* The instruction sequence :math:`\instr^\ast` must be :ref:`valid <valid-instr-seq>` with some :ref:`stack type <syntax-stacktype>` :math:`[] \to [t'^\ast]`.
1115+
1116+
* For each :ref:`operand type <syntax-opdtype>` :math:`t'_i` in :math:`{t'}^\ast` and corresponding :ref:`value type <syntax-valtype>` type :math:`t_i` in :math:`t^\ast`, :math:`t'_i` :ref:`matches <match-opdtype>` :math:`t_i`.
10861117

10871118
* Then the expression is valid with :ref:`result type <syntax-resulttype>` :math:`[t^\ast]`.
10881119

10891120
.. math::
10901121
\frac{
1091-
C \vdashinstrseq \instr^\ast : [] \to [t^\ast]
1122+
C \vdashinstrseq \instr^\ast : [] \to [{t'}^\ast]
1123+
\qquad
1124+
(\vdash t' \leq t)^\ast
10921125
}{
10931126
C \vdashexpr \instr^\ast~\END : [t^\ast]
10941127
}

Diff for: interpreter/valid/valid.ml

+6-3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ let known = List.map (fun t -> Some t)
7777
let stack ts = (NoEllipses, known ts)
7878
let (-~>) ts1 ts2 = {ins = NoEllipses, ts1; outs = NoEllipses, ts2}
7979
let (-->) ts1 ts2 = {ins = NoEllipses, known ts1; outs = NoEllipses, known ts2}
80+
let (-~>...) ts1 ts2 = {ins = Ellipses, ts1; outs = Ellipses, ts2}
8081
let (-->...) ts1 ts2 = {ins = Ellipses, known ts1; outs = Ellipses, known ts2}
8182

8283
let string_of_infer_type t =
@@ -247,9 +248,11 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type =
247248
(label c x @ [NumType I32Type]) --> label c x
248249

249250
| BrTable (xs, x) ->
250-
let ts = label c x in
251-
List.iter (fun x' -> check_stack (known ts) (known (label c x')) x'.at) xs;
252-
(ts @ [NumType I32Type]) -->... []
251+
let n = List.length (label c x) in
252+
let ts = Lib.List.table n (fun i -> peek (n - i) s) in
253+
check_stack ts (known (label c x)) x.at;
254+
List.iter (fun x' -> check_stack ts (known (label c x')) x'.at) xs;
255+
(ts @ [Some (NumType I32Type)]) -~>... []
253256

254257
| Return ->
255258
c.results -->... []

Diff for: test/core/br_table.wast

+12
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,18 @@
12501250
)
12511251
)
12521252
)
1253+
1254+
(func (export "meet-bottom")
1255+
(block (result f64)
1256+
(block (result f32)
1257+
(unreachable)
1258+
(br_table 0 1 1 (i32.const 1))
1259+
)
1260+
(drop)
1261+
(f64.const 0)
1262+
)
1263+
(drop)
1264+
)
12531265
)
12541266

12551267
(assert_return (invoke "type-i32"))

Diff for: test/core/select.wast

+13
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@
195195
(i32.wrap_i64 (select (i64.const 1) (i64.const 0) (local.get 0)))
196196
)
197197
)
198+
199+
(func (export "unreachable-num")
200+
(unreachable)
201+
(select)
202+
(i32.eqz)
203+
(drop)
204+
)
205+
(func (export "unreachable-ref")
206+
(unreachable)
207+
(select)
208+
(ref.is_null)
209+
(drop)
210+
)
198211
)
199212

200213
(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1))

Diff for: test/core/unreached-invalid.wast

-15
Original file line numberDiff line numberDiff line change
@@ -536,21 +536,6 @@
536536
"type mismatch"
537537
)
538538

539-
(assert_invalid
540-
(module (func $type-br_table-label-num-vs-label-num-after-unreachable
541-
(block (result f64)
542-
(block (result f32)
543-
(unreachable)
544-
(br_table 0 1 1 (i32.const 1))
545-
)
546-
(drop)
547-
(f64.const 0)
548-
)
549-
(drop)
550-
))
551-
"type mismatch"
552-
)
553-
554539
(assert_invalid
555540
(module (func $type-block-value-nested-unreachable-num-vs-void
556541
(block (i32.const 3) (block (unreachable)))

0 commit comments

Comments
 (0)