From d4bc208c0f62ac19529e9e734b10c17c960549e5 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 13 Dec 2019 09:56:37 +0100 Subject: [PATCH] [spec/interpreter/test] Allow nullref type externally (#66) --- document/core/appendix/index-types.rst | 3 +- document/core/binary/types.rst | 6 +- document/core/syntax/types.rst | 2 - document/core/text/types.rst | 3 +- document/js-api/index.bs | 15 ++++- interpreter/README.md | 2 +- interpreter/binary/decode.ml | 1 + interpreter/binary/encode.ml | 2 +- interpreter/text/lexer.mll | 1 + interpreter/text/parser.mly | 3 +- proposals/reference-types/Overview.md | 4 +- test/core/br_table.wast | 21 +++++- test/core/linking.wast | 90 +++++++++++++++++++++----- test/core/ref_is_null.wast | 13 ++++ test/core/ref_null.wast | 3 + test/core/select.wast | 8 +++ 16 files changed, 142 insertions(+), 35 deletions(-) diff --git a/document/core/appendix/index-types.rst b/document/core/appendix/index-types.rst index 3f13e553..37f33c7b 100644 --- a/document/core/appendix/index-types.rst +++ b/document/core/appendix/index-types.rst @@ -15,7 +15,8 @@ Category Constructor (reserved) :math:`\hex{7B}` .. :math:`\hex{71}` :ref:`Reference type ` |FUNCREF| :math:`\hex{70}` (-16 as |Bs7|) :ref:`Reference type ` |ANYREF| :math:`\hex{6F}` (-17 as |Bs7|) -(reserved) :math:`\hex{6E}` .. :math:`\hex{61}` +:ref:`Reference type ` |NULLREF| :math:`\hex{6E}` (-18 as |Bs7|) +(reserved) :math:`\hex{6D}` .. :math:`\hex{61}` :ref:`Function type ` :math:`[\valtype^\ast] \to [\valtype^\ast]` :math:`\hex{60}` (-32 as |Bs7|) (reserved) :math:`\hex{5F}` .. :math:`\hex{41}` :ref:`Result type ` :math:`[\epsilon]` :math:`\hex{40}` (-64 as |Bs7|) diff --git a/document/core/binary/types.rst b/document/core/binary/types.rst index 541f45db..e7a7f8ed 100644 --- a/document/core/binary/types.rst +++ b/document/core/binary/types.rst @@ -42,12 +42,10 @@ Reference Types \begin{array}{llclll@{\qquad\qquad}l} \production{reference type} & \Breftype &::=& \hex{70} &\Rightarrow& \FUNCREF \\ &&|& - \hex{6F} &\Rightarrow& \ANYREF \\ + \hex{6F} &\Rightarrow& \ANYREF \\ &&|& + \hex{6E} &\Rightarrow& \NULLREF \\ \end{array} -.. note:: - The type :math:`\NULLREF` cannot occur in a module. - .. index:: value type, number type, reference type pair: binary format; value type diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index 522e4795..c8543df8 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -63,8 +63,6 @@ The type |FUNCREF| denotes the infinite union of all references to :ref:`functio The type |NULLREF| only contains a single value: the :ref:`null ` reference. It is a :ref:`subtype ` of all other reference types. -By virtue of being representable in neither the :ref:`binary format ` nor the :ref:`text format `, the |NULLREF| type cannot be used in a program; -it only occurs during :ref:`validation `. .. note:: Future versions of WebAssembly may include reference types that do not include null and hence are not supertypes of |NULLREF|. diff --git a/document/core/text/types.rst b/document/core/text/types.rst index db8c6e9d..569e7f21 100644 --- a/document/core/text/types.rst +++ b/document/core/text/types.rst @@ -33,7 +33,8 @@ Reference Types \begin{array}{llcll@{\qquad\qquad}l} \production{reference type} & \Treftype &::=& \text{anyref} &\Rightarrow& \ANYREF \\ &&|& - \text{funcref} &\Rightarrow& \FUNCREF \\ + \text{funcref} &\Rightarrow& \FUNCREF \\ &&|& + \text{nullref} &\Rightarrow& \NULLREF \\ \end{array} diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 4d280fb0..87598c6f 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -152,6 +152,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df url: syntax/types.html#syntax-reftype text: anyref text: funcref + text: nullref text: function element; url: exec/runtime.html#syntax-funcelem text: import component; url: syntax/modules.html#imports text: external value; url: exec/runtime.html#syntax-externval @@ -704,6 +705,7 @@ Immediately after a WebAssembly [=memory.grow=] instruction executes, perform th
 enum TableKind {
+  "nullref",
   "anyref",
   "anyfunc",
   // Note: More values may be added in future iterations,
@@ -868,6 +870,10 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
     1. If |s| equals "i64", return [=π—‚πŸ¨πŸ¦=].
     1. If |s| equals "f32", return [=𝖿πŸ₯𝟀=].
     1. If |s| equals "f64", return [=π–ΏπŸ¨πŸ¦=].
+    1. If |s| equals "anyref", return [=anyref=].
+    1. If |s| equals "funcref", return [=funcref=].
+    1. If |s| equals "nullref", return [=nullref=].
+    1. Assert: This step is not reached.
 
 
 
@@ -876,7 +882,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each 1. If |valuetype| equals [=π—‚πŸ¨πŸ¦=], return [=π—‚πŸ¨πŸ¦.π–Όπ—ˆπ—‡π—Œπ—=] 0. 1. If |valuetype| equals [=𝖿πŸ₯𝟀=], return [=𝖿πŸ₯𝟀.π–Όπ—ˆπ—‡π—Œπ—=] 0. 1. If |valuetype| equals [=π–ΏπŸ¨πŸ¦=], return [=π–ΏπŸ¨πŸ¦.π–Όπ—ˆπ—‡π—Œπ—=] 0. - 1. Assert: This step is not reached. + 1. Else, return [=ref.null=].
@@ -1063,11 +1069,14 @@ The algorithm ToWebAssemblyValue(|v|, |type|, |error|) coerces a Java 1. If |type| is [=π–ΏπŸ¨πŸ¦=], 1. Let |f64| be ? [=ToNumber=](|v|). 1. Return [=π–ΏπŸ¨πŸ¦.π–Όπ—ˆπ—‡π—Œπ—=] |f64|. +1. Assert: |type| is a reference type. 1. If |type| is [=anyref=], - 1. Return the result of [=allocating a host address=] for |v|. + 1. Do nothing. 1. If |type| is [=funcref=], 1. If |v| is not an [=Exported function=] or null, throw |error|. - 1. Return the result of [=allocating a host address=] for |v|. +1. If |type| is [=nullref=], + 1. If |v| is not null, throw |error|. +1. Return the result of [=allocating a host address=] for |v|.
diff --git a/interpreter/README.md b/interpreter/README.md index f21239d6..77c6a543 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -184,7 +184,7 @@ align: align=(1|2|4|8|...) cvtop: trunc | extend | wrap | ... num_type: i32 | i64 | f32 | f64 -ref_type: anyref | funcref +ref_type: anyref | funcref | nullref val_type: num_type | ref_type block_type : ( result * )* func_type: ( type )? * * diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index 8b9e4d98..674240bb 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -145,6 +145,7 @@ let ref_type s = match vs7 s with | -0x10 -> FuncRefType | -0x11 -> AnyRefType + | -0x12 -> NullRefType | _ -> error s (pos s - 1) "invalid reference type" let value_type s = diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 13a93fba..f44df215 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -99,7 +99,7 @@ let encode m = let ref_type = function | FuncRefType -> vs7 (-0x10) | AnyRefType -> vs7 (-0x11) - | NullRefType -> assert false + | NullRefType -> vs7 (-0x12) let value_type = function | NumType t -> num_type t diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 3b7ec2ca..9a5cf166 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -162,6 +162,7 @@ rule token = parse | "anyref" { ANYREF } | "funcref" { FUNCREF } + | "nullref" { NULLREF } | (nxx as t) { NUM_TYPE (num_type t) } | "mut" { MUT } diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 6257c3dd..c42b3cd3 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -147,7 +147,7 @@ let inline_type_explicit (c : context) x ft at = %token LPAR RPAR %token NAT INT FLOAT STRING VAR -%token ANYREF FUNCREF NUM_TYPE MUT +%token ANYREF NULLREF FUNCREF NUM_TYPE MUT %token UNREACHABLE NOP DROP SELECT %token BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE %token CALL CALL_INDIRECT RETURN @@ -209,6 +209,7 @@ string_list : ref_type : | ANYREF { AnyRefType } | FUNCREF { FuncRefType } + | NULLREF { NullRefType } value_type : | NUM_TYPE { NumType $1 } diff --git a/proposals/reference-types/Overview.md b/proposals/reference-types/Overview.md index bc32ffd6..e0511129 100644 --- a/proposals/reference-types/Overview.md +++ b/proposals/reference-types/Overview.md @@ -26,7 +26,7 @@ Get the most important parts soon! Summary: -* Add a new type `anyref` that can be used as both a value type and a table element type. +* Add new types `anyref` and `nullref` that can be used as both a value types and a table element types. * Also allow `funcref` as a value type. @@ -49,8 +49,6 @@ Typing extensions: * Introduce `anyref`, `funcref`, and `nullref` as a new class of *reference types*. - `reftype ::= anyref | funcref | nullref` - - `nullref` is merely an internal type and is neither expressible in the binary format, nor the text format, nor the JS API. - - Question: should it be? * Value types (of locals, globals, function parameters and results) can now be either numeric types or reference types. - `numtype ::= i32 | i64 | f32 | f64` diff --git a/test/core/br_table.wast b/test/core/br_table.wast index 11b51a71..4ac79de6 100644 --- a/test/core/br_table.wast +++ b/test/core/br_table.wast @@ -1258,7 +1258,6 @@ ) ) ) - (func (export "meet-funcref-2") (param i32) (result anyref) (block $l1 (result anyref) (block $l2 (result funcref) @@ -1266,7 +1265,6 @@ ) ) ) - (func (export "meet-funcref-3") (param i32) (result anyref) (block $l1 (result anyref) (block $l2 (result funcref) @@ -1274,7 +1272,6 @@ ) ) ) - (func (export "meet-funcref-4") (param i32) (result anyref) (block $l1 (result anyref) (block $l2 (result funcref) @@ -1283,6 +1280,24 @@ ) ) + (func (export "meet-nullref") (param i32) (result funcref) + (block $l1 (result funcref) + (block $l2 (result nullref) + (br_table $l1 $l2 $l1 (ref.null) (local.get 0)) + ) + ) + ) + + (func (export "meet-multi-ref") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (block $l3 (result nullref) + (br_table $l3 $l2 $l1 (ref.null) (local.get 0)) + ) + ) + ) + ) + (func (export "meet-bottom") (block (result f64) (block (result f32) diff --git a/test/core/linking.wast b/test/core/linking.wast index be561e51..e72f928b 100644 --- a/test/core/linking.wast +++ b/test/core/linking.wast @@ -94,29 +94,63 @@ (module $Mref_ex - (global (export "g-const") funcref (ref.null)) - (global (export "g-var") (mut funcref) (ref.null)) + (global (export "g-const-null") nullref (ref.null)) + (global (export "g-var-null") (mut nullref) (ref.null)) + (global (export "g-const-func") funcref (ref.null)) + (global (export "g-var-func") (mut funcref) (ref.null)) + (global (export "g-const-any") anyref (ref.null)) + (global (export "g-var-any") (mut anyref) (ref.null)) ) (register "Mref_ex" $Mref_ex) (module $Mref_im - (global (import "Mref_ex" "g-const") anyref) + (global (import "Mref_ex" "g-const-null") nullref) + (global (import "Mref_ex" "g-const-null") funcref) + (global (import "Mref_ex" "g-const-null") anyref) + (global (import "Mref_ex" "g-const-func") funcref) + (global (import "Mref_ex" "g-const-func") anyref) + (global (import "Mref_ex" "g-const-any") anyref) + + (global (import "Mref_ex" "g-var-null") (mut nullref)) + (global (import "Mref_ex" "g-var-func") (mut funcref)) + (global (import "Mref_ex" "g-var-any") (mut anyref)) ) (assert_unlinkable - (module (global (import "Mref_ex" "g-var") (mut anyref))) + (module (global (import "Mref_ex" "g-const-func") nullref)) "incompatible import type" ) - - -(module $Mglobal_ex - (func $f) - (global (export "g") anyref (ref.func $f)) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-any") nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-any") funcref)) + "incompatible import type" ) -(register "Mglobal_ex" $Mglobal_ex) (assert_unlinkable - (module (global (import "Mglobal_ex" "g") funcref)) + (module (global (import "Mref_ex" "g-var-null") (mut funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-null") (mut anyref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut nullref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut anyref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-any") (mut nullref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-any") (mut funcref))) "incompatible import type" ) @@ -278,14 +312,40 @@ (module $Mtable_ex - (func $f) - (table $t (export "t") 1 anyref) - (elem (i32.const 0) $f) + (table $t1 (export "t-null") 1 nullref) + (table $t2 (export "t-func") 1 funcref) + (table $t3 (export "t-any") 1 anyref) ) (register "Mtable_ex" $Mtable_ex) +(module + (table (import "Mtable_ex" "t-null") 1 nullref) + (table (import "Mtable_ex" "t-func") 1 funcref) + (table (import "Mtable_ex" "t-any") 1 anyref) +) + +(assert_unlinkable + (module (table (import "Mtable_ex" "t-null") 1 funcref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-null") 1 anyref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-func") 1 nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-func") 1 anyref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-any") 1 nullref)) + "incompatible import type" +) (assert_unlinkable - (module (table (import "Mtable_ex" "t") 1 funcref)) + (module (table (import "Mtable_ex" "t-any") 1 funcref)) "incompatible import type" ) diff --git a/test/core/ref_is_null.wast b/test/core/ref_is_null.wast index 7edc8e28..3e9a6783 100644 --- a/test/core/ref_is_null.wast +++ b/test/core/ref_is_null.wast @@ -1,4 +1,7 @@ (module + (func $f1 (export "nullref") (param $x nullref) (result i32) + (ref.is_null (local.get $x)) + ) (func $f2 (export "anyref") (param $x anyref) (result i32) (ref.is_null (local.get $x)) ) @@ -6,6 +9,7 @@ (ref.is_null (local.get $x)) ) + (table $t1 2 nullref) (table $t2 2 anyref) (table $t3 2 funcref) (elem $t3 (i32.const 1) $dummy) (func $dummy) @@ -14,10 +18,14 @@ (table.set $t2 (i32.const 1) (local.get $r)) ) (func (export "deinit") + (table.set $t1 (i32.const 1) (ref.null)) (table.set $t2 (i32.const 1) (ref.null)) (table.set $t3 (i32.const 1) (ref.null)) ) + (func (export "nullref-elem") (param $x i32) (result i32) + (call $f1 (table.get $t1 (local.get $x))) + ) (func (export "anyref-elem") (param $x i32) (result i32) (call $f2 (table.get $t2 (local.get $x))) ) @@ -26,6 +34,7 @@ ) ) +(assert_return (invoke "nullref" (ref.null)) (i32.const 1)) (assert_return (invoke "anyref" (ref.null)) (i32.const 1)) (assert_return (invoke "funcref" (ref.null)) (i32.const 1)) @@ -33,16 +42,20 @@ (invoke "init" (ref.host 0)) +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) (assert_return (invoke "anyref-elem" (i32.const 0)) (i32.const 1)) (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "nullref-elem" (i32.const 1)) (i32.const 1)) (assert_return (invoke "anyref-elem" (i32.const 1)) (i32.const 0)) (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) (invoke "deinit") +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) (assert_return (invoke "anyref-elem" (i32.const 0)) (i32.const 1)) (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) (assert_return (invoke "anyref-elem" (i32.const 1)) (i32.const 1)) (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) diff --git a/test/core/ref_null.wast b/test/core/ref_null.wast index 30384802..96cac314 100644 --- a/test/core/ref_null.wast +++ b/test/core/ref_null.wast @@ -1,10 +1,13 @@ (module (func (export "anyref") (result anyref) (ref.null)) (func (export "funcref") (result funcref) (ref.null)) + (func (export "nullref") (result nullref) (ref.null)) (global anyref (ref.null)) (global funcref (ref.null)) + (global nullref (ref.null)) ) (assert_return (invoke "anyref") (ref.null)) (assert_return (invoke "funcref") (ref.null)) +(assert_return (invoke "nullref") (ref.null)) diff --git a/test/core/select.wast b/test/core/select.wast index 33b7f624..090b052b 100644 --- a/test/core/select.wast +++ b/test/core/select.wast @@ -29,6 +29,12 @@ (func (export "select-f64-t") (param f64 f64 i32) (result f64) (select (result f64) (local.get 0) (local.get 1) (local.get 2)) ) + (func (export "select-nullref") (param nullref nullref i32) (result nullref) + (select (result nullref) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-funcref") (param funcref funcref i32) (result funcref) + (select (result funcref) (local.get 0) (local.get 1) (local.get 2)) + ) (func (export "select-anyref") (param anyref anyref i32) (result anyref) (select (result anyref) (local.get 0) (local.get 1) (local.get 2)) ) @@ -244,6 +250,8 @@ (assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) (assert_return (invoke "select-f32-t" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) (assert_return (invoke "select-f64-t" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) +(assert_return (invoke "select-nullref" (ref.null) (ref.null) (i32.const 1)) (ref.null)) +(assert_return (invoke "select-funcref" (ref.null) (ref.null) (i32.const 1)) (ref.null)) (assert_return (invoke "select-anyref" (ref.host 1) (ref.host 2) (i32.const 1)) (ref.host 1)) (assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2))