Skip to content
This repository was archived by the owner on Nov 3, 2021. It is now read-only.

Commit b65aab6

Browse files
committed
[spec] Reintroduce bottom type
1 parent 8f5038e commit b65aab6

File tree

7 files changed

+90
-38
lines changed

7 files changed

+90
-38
lines changed

document/core/appendix/algorithm.rst

Lines changed: 11 additions & 8 deletions
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`.

document/core/appendix/index-rules.rst

Lines changed: 2 additions & 2 deletions
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`

document/core/valid/instructions.rst

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,41 @@
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+
: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+
919
The types describe the required input stack with argument values of 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.
1121

22+
.. _match-opdtype:
23+
24+
Stack types are akin to :ref:`function types <syntax-functype>`,
25+
except that they allow individual operands to be classified as :math:`\bot`, indicating that the type is unconstrained.
26+
As an auxiliary notion, an :ref:`operand type <syntax-opdtype>` :math:`t_1` *matches* another operand type :math:`t_2`, if :math:`t_1` is either :math:`\bot` or equal to :math:`t_2`.
27+
28+
.. math::
29+
\frac{
30+
}{
31+
\vdash t \leq t
32+
}
33+
\qquad
34+
\frac{
35+
}{
36+
\vdash \bot \leq t
37+
}
38+
1239
.. note::
1340
For example, the instruction :math:`\I32.\ADD` has type :math:`[\I32~\I32] \to [\I32]`,
1441
consuming two |I32| values and producing one.
@@ -254,7 +281,7 @@ Parametric Instructions
254281

255282
* Else:
256283

257-
* The instruction is valid with type :math:`[t~t~\I32] \to [t]`, for any :ref:`number type <syntax-numtype>` :math:`t`.
284+
* 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>`.
258285

259286
.. math::
260287
\frac{
@@ -263,7 +290,7 @@ Parametric Instructions
263290
}
264291
\qquad
265292
\frac{
266-
t = \numtype
293+
\vdash t \leq \numtype
267294
}{
268295
C \vdashinstr \SELECT : [t~t~\I32] \to [t]
269296
}
@@ -1033,7 +1060,7 @@ Empty Instruction Sequence: :math:`\epsilon`
10331060
............................................
10341061

10351062
* 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`.
1063+
for any sequence of :ref:`operand types <syntax-opdtype>` :math:`t^\ast`.
10371064

10381065
.. math::
10391066
\frac{
@@ -1052,13 +1079,18 @@ Non-empty Instruction Sequence: :math:`\instr^\ast~\instr_N`
10521079
for some sequences of :ref:`value types <syntax-valtype>` :math:`t^\ast` and :math:`t_3^\ast`.
10531080

10541081
* 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`.
1082+
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`.
1083+
1084+
* For each :ref:`operand type <syntax-opdtype>` :math:`t'_i` in :math:`{t'}^\ast` and corresponding type :math:`t_i` in :math:`t^\ast`,
1085+
:math:`t'_i` :ref:`matches <match-opdtype>` :math:`t_i`.
10561086

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

10591089
.. math::
10601090
\frac{
1061-
C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~t^\ast]
1091+
C \vdashinstrseq \instr^\ast : [t_1^\ast] \to [t_0^\ast~{t'}^\ast]
1092+
\qquad
1093+
(\vdash t' \leq t)^\ast
10621094
\qquad
10631095
C \vdashinstr \instr_N : [t^\ast] \to [t_3^\ast]
10641096
}{
@@ -1081,14 +1113,18 @@ Expressions :math:`\expr` are classified by :ref:`result types <syntax-resulttyp
10811113
:math:`\instr^\ast~\END`
10821114
........................
10831115

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]`.
1116+
* The instruction sequence :math:`\instr^\ast` must be :ref:`valid <valid-instr-seq>` with some :ref:`stack type <syntax-stacktype>` :math:`[] \to [t'^\ast]`.
1117+
1118+
* 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`,
1119+
:math:`t'_i` :ref:`matches <match-opdtype>` :math:`t_i`.
10861120

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

10891123
.. math::
10901124
\frac{
1091-
C \vdashinstrseq \instr^\ast : [] \to [t^\ast]
1125+
C \vdashinstrseq \instr^\ast : [] \to [{t'}^\ast]
1126+
\qquad
1127+
(\vdash t' \leq t)^\ast
10921128
}{
10931129
C \vdashexpr \instr^\ast~\END : [t^\ast]
10941130
}

interpreter/valid/valid.ml

Lines changed: 6 additions & 3 deletions
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 -->... []

test/core/br_table.wast

Lines changed: 12 additions & 0 deletions
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"))

test/core/select.wast

Lines changed: 13 additions & 0 deletions
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))

test/core/unreached-invalid.wast

Lines changed: 0 additions & 15 deletions
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)