blob: f9c098d0fa004bce4c0bcb6bb5549ff9d1225ac9 [file] [log] [blame] [edit]
;; This file tests validation only, focusing on GC types and subtyping.
;;;;
;;;;; WasmFX types
;;;;
(module
(type $f (func))
(type $ft1 (sub (func (param (ref $f)) (result (ref func)))))
(type $ct1 (sub (cont $ft1)))
(type $ft2 (func (param (ref any)) (result (ref func))))
(type $ct2 (cont $ft2))
(type $ft3 (sub $ft1 (func (param (ref func)) (result (ref $f)))))
(type $ct3 (cont $ft3))
;; Okay: Continuation types are covariant, have declared $ft3 <: $ft1
(type $ct_sub (sub $ct1 (cont $ft3)))
(func $test
(param $p1 (ref $ct1))
(param $p2 (ref $ct_sub))
;; Okay: (ref $ct_sub) <: (ref $ct1)
(local.set $p1 (local.get $p2))
)
)
(assert_invalid
(module
(type $f (func))
(type $ft1 (sub (func (param (ref $f)) (result (ref func)))))
(type $ct1 (sub (cont $ft1)))
(type $ft2 (func (param (ref func)) (result (ref $f))))
;; Error: $ft2 and ft1 have compatible types, but have not declared $ft2 <: ft1
(type $error (sub $ct1 (cont $ft2)))
)
"sub type 4 does not match super type 2"
)
(assert_invalid
(module
(type $f (func))
(type $ft1 (sub (func (param (ref $f)) (result (ref func)))))
(type $ct1 (sub (cont $ft1)))
(type $ft2 (func (param (ref func)) (result (ref $f))))
(type $ct2 (cont $ft2))
(func $test
(param $p1 (ref $ct1))
(param $p2 (ref $ct2))
;; Error $ct2 and $ct1 have generally compatible types,
;; but have not declared $ft2 <: ft1 or $ct2 <: $ct1
;; Thus, $ct2 </: $ct1.
(local.set $p1 (local.get $p2))
)
)
"type mismatch"
)
(assert_invalid
(module
(type $f (func))
(type $ft1 (sub (func (param (ref $f)) (result (ref func)))))
(type $ct1 (sub (cont $ft1)))
(type $ft3 (sub $ft1 (func (param (ref func)) (result (ref $f)))))
(type $ct3 (cont $ft3))
(func $error
(param $p1 (ref $ct1))
;;(param $p2 (ref $ct2))
(param $p3 (ref $ct3))
;; Error $ct3 and $ct1 have generally compatible types,
;; (in particular: declared $ft3 <: ft1,
;; but have not declared $ct3 <: $ct1
;; $ct3 </: $ct1
(local.set $p1 (local.get $p3))
)
)
"type mismatch"
)
(assert_invalid
(module
(func $error
(param $p_any (ref any))
(param $p_cont (ref cont))
;; Error: cont </: any
(local.set $p_any (local.get $p_cont))
)
)
"type mismatch"
)
;;;;
;;;; cont_bind instructions
;;;;
(module $non_final
(type $ft1 (func (param i32) (result (ref func))))
(type $ct1 (sub (cont $ft1)))
(type $ft0 (func (result (ref func))))
(type $ct0 (sub (cont $ft0)))
(func $test1 (param $x (ref $ct1))
(i32.const 123)
(local.get $x)
;; Smoke test: using non-final types here
(cont.bind $ct1 $ct0)
(drop)
)
)
(module $subtyping0
(type $f (func))
(type $ft0_sup (func (result (ref func))))
(type $ct0_sup (cont $ft0_sup))
(type $ft1 (func (param i32) (result (ref $f))))
(type $ct1 (cont $ft1))
(type $ft0 (func (result (ref $f))))
(type $ct0 (cont $ft0))
(func $test2 (param $x (ref $ct1))
(i32.const 123)
(local.get $x)
;; Okay: The most natural second continuation type would be $ct0.
;; But we have (func (result (ref $f))) <: (func (result (ref $func)))
;; This holds without us needing to declare any subtyping relations at all.
(cont.bind $ct1 $ct0_sup)
(drop)
)
)
(module $recursive
(rec
(type $ft0 (func (result (ref $ct_rec))))
(type $ft1 (func (param i32) (result (ref $ct_rec))))
(type $ct_rec (cont $ft1))
)
(type $ct0 (cont $ft0))
(rec
(type $ft0' (func (result (ref $ct_rec'))))
(type $ft1' (func (param i32) (result (ref $ct_rec'))))
(type $ct_rec' (cont $ft1'))
)
(type $ct1 (cont $ft1'))
;; Okay: Some simple test where the types involved in cont.bind
;; are part of a recursion group
;; (more concretely: two equivalent recursion groups)
(func $test (param $x (ref $ct1))
(i32.const 123)
(local.get $x)
(cont.bind $ct1 $ct0)
(drop)
)
)
(module $recursive_subtyping
;; We define types such that $ft0 <: $ft0_sup and $ct_rec <: $ct_rec_sup
(rec
(type $ft0_sup (sub (func (result (ref $ct_rec_sup)))))
(type $ft0 (sub $ft0_sup (func (result (ref $ct_rec)))))
(type $ft1 (sub (func (param i32) (result (ref $ct_rec)))))
(type $ct_rec_sup (sub (cont $ft0_sup)))
(type $ct_rec (sub $ct_rec_sup (cont $ft0)))
)
(type $ct0_sup (cont $ft0_sup))
(type $ct0 (cont $ft0))
(type $ct1 (cont $ft1))
(func $test (param $x (ref $ct1))
(i32.const 123)
(local.get $x)
(cont.bind $ct1 $ct0)
(drop)
(i32.const 123)
(local.get $x)
;; Okay: We have (func (result (ref $ct_rec))) <: (func (result (ref
;; $ct_rec_sup)))
(cont.bind $ct1 $ct0_sup)
(drop)
)
)
;;;;
;;;; resume instructions
;;;;
(module $subtyping_resume0
(type $ft0 (func))
(type $ct0 (cont $ft0))
(type $ft_sup (func (param (ref $ft0))))
(type $ct_sup (cont $ft_sup))
(type $ft_sub (func (param (ref func))))
(type $ct_sub (cont $ft_sub)) ;; unused
(tag $t (result (ref func)))
(func $test0
(param $p (ref $ct0))
;; Here we test subtyping with respect to the continuations received by handlers.
;;
;; The most "straightforward" type of the continuation in $handler would be (ref
;; $ct_sub). Instead, we use $ct_sup. According to the spec, we neither need
;; to declare $ft_sub <: $ft_sup or $ct_sub <: $ct_sup for this to work. We
;; have (func (param (ref func))) <: (func (param (ref $ft0))), which is
;; sufficient
(block $handler (result (ref $ct_sup))
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
(module $subtyping_resume1
(type $ft0 (func))
(type $ct0 (cont $ft0))
(tag $t (param (ref $ft0)))
(func $test0
(param $p (ref $ct0))
;; Here we test subtyping with respect to the payloads received by handlers.
;; Instead of just (ref $ft0), then handler takes func.
(block $handler (result (ref func) (ref $ct0))
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
(assert_invalid
(module
(type $ft0 (func))
(type $ct0 (cont $ft0))
(type $ft_sup (sub (func (param (ref $ft0)))))
(type $ct_sup (sub (cont $ft_sup)))
(type $ft_sub (sub $ft_sup (func (param (ref func)))))
(type $ct_sub (cont $ft_sub))
(tag $t (param (ref $ct_sub)))
(func $test0
(param $p (ref $ct0))
;; This is similar to $subtyping1, but this time we use a continuation as payload.
;; But we did not actually declare $ct_sub <: $ct_sub.
;;
;; This is mostly just to check the following:
;; For the continuation received by every handler, we see through the
;; continuation type and do structural subtyping on the underlying
;; function type.
;; However, for continuations that are just payloads ($ct_sup here), we do
;; ordinary nominal subtyping.
(block $handler (result (ref $ct_sup) (ref $ct0))
(local.get $p)
(resume $ct0 (on $t $handler))
(return)
)
(unreachable)
)
)
"type mismatch"
)