blob: 116b3d17f188fc83fa2385e8943985e826fdeb98 [file] [log] [blame] [edit]
;; This file tests validation only, without GC types and subtyping.
;;;;
;;;; WasmFX types
;;;;
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; nocont <: cont
(local.set $x1 (local.get $p2))
;; nocont <: $ct1
(local.set $x3 (local.get $p2))
;; $ct1 <: $cont
(local.set $x3 (local.get $p3))
;; (ref $ct1) <: (ref null $cont)
(local.set $x5 (local.get $p3))
)
)
(assert_invalid
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; cont </: nocont
(local.set $p2 (local.get $p1))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; $ct1 </: nocont
(local.set $p2 (local.get $p3))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; $cont </: $ct1
(local.set $p3 (local.get $p1))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(param $p4 (ref $ct2))
(param $p5 (ref null $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; (ref null $ct1) </: (ref $cont)
(local.set $x3 (local.get $p5))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref cont))
(param $p2 (ref nocont))
(param $p3 (ref $ct1))
(param $p4 (ref $ct2))
(param $p5 (ref null $ct1))
(local $x1 (ref cont))
(local $x2 (ref nocont))
(local $x3 (ref $ct1))
(local $x4 (ref $ct2))
(local $x5 (ref null $ct1))
;; (ref $ct1) </: (ref $ct2)
(local.set $p4 (local.get $p3))
)
)
"type mismatch"
)
;;;;
;;;; cont_bind instructions
;;;;
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i64 i32) (result i32)))
(type $ct2 (cont $ft2))
(func $test1
(param $p_ct1 (ref null $ct1))
(result (ref $ct0))
;; cont.bind takes nullable ref, returns non-nullable one
(i32.const 123)
(local.get $p_ct1)
(cont.bind $ct1 $ct0)
)
(func $test2
(param $p_ct2 (ref $ct2))
(result (ref $ct1))
;; cont.bind does not have to apply continuation fully
(i64.const 123)
(local.get $p_ct2)
(cont.bind $ct2 $ct1)
)
(func $test3
(param $p_ct1 (ref $ct1))
(result (ref $ct1))
;; cont.bind can take same type twice, not actually apply anything
(local.get $p_ct1)
(cont.bind $ct1 $ct1)
)
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(func $error
(param $p_ft0 (ref $ft0))
;; error: non-continuation type on cont.bind
(local.get $p_ft0)
(cont.bind $ft0 $ft0)
(drop)
)
)
"non-continuation type"
)
;; cont.new type mismatch: passing a continuation reference to cont.new instead of function reference
;; This is actually just checking type-matching, not cont.new specific:
;; (ref null $ct1) is not a subtype of (ref null $ft1)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $error
(param $p_ct1 (ref $ct1))
;; cont.bind type mismatch: passing wrong kind of continuation.
;; This is actually just checking type-matching, not cont.bind specific.
(local.get $p_ct1)
(cont.bind $ct0 $ct0)
(drop)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i64 i32) (result i32)))
(type $ct2 (cont $ft2))
(type $ft1_alt (func (param i64) (result i32)))
(type $ct1_alt (cont $ft1_alt))
(func $error
(param $p_ct2 (ref $ct2))
;; error: two continuation types not agreeing on arg types
(i64.const 123)
(local.get $p_ct2)
(cont.bind $ct2 $ct1_alt)
(drop)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i64 i32) (result i32)))
(type $ct2 (cont $ft2))
(type $ft1_alt (func (param i32) (result i64)))
(type $ct1_alt (cont $ft1_alt))
(func $error
(param $p_ct2 (ref $ct2))
;; error: two continuation types not agreeing on return types
(i64.const 123)
(local.get $p_ct2)
(cont.bind $ct2 $ct1_alt)
(drop)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $error
(param $p_ct0 (ref $ct0))
;; error: trying to go from 0 to 1 args
(local.get $p_ct0)
(cont.bind $ct0 $ct1)
(drop)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $error
(param $p_ct1 (ref $ct1))
;; error: Insufficient arguments::
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(cont.bind $ct1 $ct0 (local.get $p_ct1))
(drop)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (result i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $error
(param $p_ct1 (ref $ct1))
;; error: Too many arguments::
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(cont.bind $ct1 $ct0 (i32.const 123) (i32.const 123) (local.get $p_ct1))
(drop)
)
)
"type mismatch"
)
;;;;
;;;; cont_new instructions
;;;;
(module
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32 i32) (result i32)))
(type $ct2 (cont $ft2))
(func $f1 (export "f1") (param i32) (result i32)
(i32.const 123)
)
(func $drop_ct1 (param $c (ref $ct1)))
;; simple smoke test
(func $test_good1 (param $x (ref $ft1)) (result (ref $ct1))
(local.get $x)
(cont.new $ct1)
)
;; cont.new takes a nullable function
(func $test_good2 (param $x (ref null $ft1)) (result (ref $ct1))
(local.get $x)
(cont.new $ct1)
)
)
(assert_invalid
(module
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $bad (param $x (ref null $ct1)) (result (ref $ct1))
(local.get $x)
(cont.new $ct1)
)
)
;; cont.new type mismatch: passing a continuation reference to cont.new instead of function reference
;; This is actually just checking type-matching, not cont.new specific:
;; (ref null $ct1) is not a subtype of (ref null $ft1)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i32 i32) (result i32)))
(type $ct2 (cont $ft2))
(func $drop_ct1 (param $c (ref $ct1)))
(func $bad (param $x (ref null $ft2)) (result (ref $ct1))
(local.get $x)
(cont.new $ct1)
)
)
;; cont.new type mismatch: passing function of wrong type to cont.new
;; This is actually just checking type-matching, not cont.new specific:
;; (ref null $ft2) is not a subtype of (ref null $ft1)
"type mismatch"
)
(assert_invalid
(module
(type $ft1 (func (param i32) (result i32)))
(type $ct1 (cont $ft1))
(func $bad (param $x (ref null $ct1))
(local.get $x)
(cont.new $ft1)
(drop)
)
)
;; non-continuation type on cont.new
"non-continuation type 0"
)
;;;;
;;;; resume instructions
;;;;
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(type $ft1 (func (param f32) (result f64)))
(type $ct1 (cont $ft1))
(type $ft2 (func (param i64) (result f64)))
(type $ct2 (cont $ft2))
(type $ft3 (func (param i32) (result f64)))
(type $ct3 (cont $ft3))
(tag $t0)
(tag $t1)
(tag $t2 (param i32) (result i64))
(tag $t3 (param i64) (result i32))
;; Multiple tags, all types handled correctly
(func $test0 (param $x (ref $ct1)) (result f64)
(block $handler0 (result i32 (ref $ct2))
(block $handler1 (result i64 (ref $ct3))
(f32.const 1.23)
(local.get $x)
(resume $ct1 (on $t2 $handler0) (on $t3 $handler1))
(return)
)
(unreachable)
)
(unreachable)
)
;; Same as $test0, but we provide two handlers for the same tag
(func $test1 (param $x (ref $ct1)) (result f64)
(block $handler0 (result i32 (ref $ct2))
(block $handler1 (result i32 (ref $ct2))
(f32.const 1.23)
(local.get $x)
(resume $ct1 (on $t2 $handler0) (on $t2 $handler1))
(return)
)
(unreachable)
)
(unreachable)
)
;; resume takes a nullable reference
(func $test2 (param $x (ref null $ct0))
(local.get $x)
(resume $ct0)
)
;; handler blocks take the continuation as nullable ref
(func $test3 (param $x (ref $ct0))
(block $handler (result (ref null $ct0))
(local.get $x)
(resume $ct0 (on $t0 $handler))
(return)
)
(unreachable)
)
;; Nothing wrong with using the same handler block for multiple tags
(func $test4 (param $x (ref $ct0))
(block $handler (result (ref null $ct0))
(local.get $x)
(resume $ct0 (on $t0 $handler) (on $t1 $handler))
(return)
)
(unreachable)
)
;; handler block can have params
(func $test5 (param $x (ref $ct0))
(local.get $x)
(block $handler (param (ref $ct0)) (result (ref $ct0))
(resume $ct0 (on $t0 $handler))
(return)
)
(unreachable)
)
)
;; tag does not exist
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
;;(tag $t0)
(func $error (param $x (ref $ct0))
(block $handler (result (ref null $ct0))
(local.get $x)
(resume $ct0 (on 0 $handler))
(return)
)
(unreachable)
)
)
"unknown tag"
)
;; non-continuation type on resume
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(func $error (param $x (ref $ct0))
(local.get $x)
(resume $ft0)
)
)
"non-continuation type"
)
;; non-continuation type on handler
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(tag $t0)
(func $test1 (param $x (ref $ct0))
(block $handler (result (ref $ft0))
(local.get $x)
(resume $ct0 (on $t0 $handler))
(return)
)
(unreachable)
)
)
"non-continuation type"
)
(assert_invalid
(module
(type $ft (func (param i32)))
(type $ct (cont $ft))
(func $error
(param $p (ref $ct))
;; error: Too many arguments::
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(resume $ct (i32.const 123) (i32.const 123) (local.get $p))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft (func (param i32)))
(type $ct (cont $ft))
(func $error
(param $p (ref $ct))
;; error: Too few arguments::
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(resume $ct (local.get $p))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func (param i32)))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i64)))
(type $ct1 (cont $ft1))
(func $error
(param $p (ref $ct1))
;; error: Mismatch between annotation on resume and actual argument
;; This is really testing the general application of arguments to instructions.
(resume $ct0 (i32.const 123) (local.get $p))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(tag $t (param i32))
(func $error
(param $p (ref $ct0))
(block $handler (result (ref $ct0))
;; error: handler block has insufficient number of results
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(tag $t (param i32))
(func $error
(param $p (ref $ct0))
(block $handler (result i32 i32 (ref $ct0))
;; error: handler block has too many results
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(tag $t (param i32))
(func $error
(param $p (ref $ct0))
(block $handler (result i64 (ref $ct0))
;; error: type mismatch in non-continuation handler result type
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
"type mismatch"
)
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(type $ft1 (func (param i32)))
(type $ct1 (cont $ft1))
(tag $t (param i32))
(func $error
(param $p (ref $ct0))
(block $handler (result i32 (ref $ct1))
;; error: type mismatch in continuation handler result type
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
"type mismatch"
)
;;;;
;;;; suspend instructions
;;;;
(module
(tag $t (param i64 i32) (result i32 i64))
(func $test (result i32 i64)
(i64.const 123)
(i32.const 123)
(suspend $t)
)
)
(assert_invalid
(module
(tag $t (param i64 i32) (result i32 i64))
(func $test (result i32 i64)
;; error: Insufficient arguments::
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(suspend $t (i64.const 123))
)
)
"type mismatch"
)
(assert_invalid
(module
(tag $t (param i32) (result i32 i64))
(func $test (result i32 i64)
;; error: Too many arguments:
;; This is really testing the general application of arguments to instructions,
;; but trying to trick parsers of the folded form
(suspend $t (i64.const 123) (i32.const 123))
)
)
"type mismatch"
)
(assert_invalid
(module
(func $test
;; error: Tag does not exist
(suspend 0)
)
)
"unknown tag"
)
;; Illegal casts
(assert_invalid
(module
(func (drop (ref.test contref (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(func (drop (ref.test nullcontref (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(type $f (func))
(type $c (cont $f))
(func (drop (ref.test (ref $c) (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(func (drop (ref.cast contref (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(func (drop (ref.cast nullcontref (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(type $f (func))
(type $c (cont $f))
(func (drop (ref.cast (ref $c) (unreachable))))
)
"invalid cast"
)
(assert_invalid
(module
(func
(block (result contref) (br_on_cast 0 contref contref (unreachable)))
(drop)
)
)
"invalid cast"
)
(assert_invalid
(module
(func
(block (result contref) (br_on_cast 0 nullcontref nullcontref (unreachable)))
(drop)
)
)
"invalid cast"
)
(assert_invalid
(module
(type $f (func))
(type $c (cont $f))
(func
(block (result contref) (br_on_cast 0 (ref $c) (ref $c) (unreachable)))
(drop)
)
)
"invalid cast"
)
(assert_invalid
(module
(func
(block (result contref) (br_on_cast_fail 0 contref contref (unreachable)))
(drop)
)
)
"invalid cast"
)
(assert_invalid
(module
(func
(block (result contref) (br_on_cast_fail 0 nullcontref nullcontref (unreachable)))
(drop)
)
)
"invalid cast"
)
(assert_invalid
(module
(type $f (func))
(type $c (cont $f))
(func
(block (result contref) (br_on_cast_fail 0 (ref $c) (ref $c) (unreachable)))
(drop)
)
)
"invalid cast"
)