blob: fd11ecfe92a78a4b015530a5f03c3072114341fd [file]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --closed-world --preserve-type-order \
;; RUN: --unsubtyping --remove-unused-types -all -S -o - | filecheck %s
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (func)))
(type $super (sub (func)))
;; CHECK: (type $sub (sub $super (func)))
(type $sub (sub $super (func)))
;; CHECK: (type $cont (cont $super))
(type $cont (cont $super))
;; CHECK: (elem declare func $cont-new)
;; CHECK: (func $cont-new (type $sub)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (cont.new $cont
;; CHECK-NEXT: (ref.func $cont-new)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $cont-new (type $sub)
(drop
;; This requires $sub <: $super.
(cont.new $cont
(ref.func $cont-new)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $one (func (param (ref $super))))
(type $one (func (param (ref $super))))
;; CHECK: (type $none (func))
(type $none (func))
;; CHECK: (type $cont-one (cont $one))
(type $cont-one (cont $one))
;; CHECK: (type $cont-none (cont $none))
(type $cont-none (cont $none))
;; CHECK: (func $cont-bind (type $none)
;; CHECK-NEXT: (local $one (ref null $cont-one))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (cont.bind $cont-one $cont-none
;; CHECK-NEXT: (struct.new_default $sub)
;; CHECK-NEXT: (local.get $one)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $cont-bind
(local $one (ref null $cont-one))
(drop
;; This requires $sub <: $super.
(cont.bind $cont-one $cont-none
(struct.new $sub)
(local.get $one)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $2 (func (param (ref null $super))))
;; CHECK: (type $3 (func))
;; CHECK: (tag $e (type $2) (param (ref null $super)))
(tag $e (param (ref null $super)))
;; CHECK: (func $suspend (type $3)
;; CHECK-NEXT: (suspend $e
;; CHECK-NEXT: (struct.new_default $sub)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $suspend
;; This requires $sub <: $super.
(suspend $e
(struct.new $sub)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func (param (ref null $super))))
(type $f (func (param (ref null $super))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $4 (func))
;; CHECK: (func $resume-param (type $4)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (resume $cont
;; CHECK-NEXT: (struct.new_default $sub)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-param
(local $cont (ref null $cont))
;; This requires $sub <: $super.
(resume $cont
(struct.new $sub)
(local.get $cont)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func))
(type $f (func))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $4 (func (param (ref null $sub))))
;; CHECK: (type $5 (func (result (ref null $super) (ref null $cont))))
;; CHECK: (tag $e (type $4) (param (ref null $sub)))
(tag $e (param (ref null $sub)))
;; CHECK: (func $resume-label (type $f)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (tuple.drop 2
;; CHECK-NEXT: (block $l (type $5) (result (ref null $super) (ref null $cont))
;; CHECK-NEXT: (resume $cont (on $e $l)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-label
(local $cont (ref null $cont))
(tuple.drop 2
(block $l (result (ref null $super) (ref null $cont))
;; Sending the tag parameter to the block requires $sub <: $super.
(resume $cont (on $e $l)
(local.get $cont)
)
(unreachable)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func))
(type $f (func))
;; CHECK: (type $next-f (func (param (ref null $sub))))
(type $next-f (func (param (ref null $sub))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $next-cont (cont $next-f))
(type $next-cont (cont $next-f))
;; CHECK: (type $6 (func (result (ref null $super))))
;; CHECK: (tag $e (type $6) (result (ref null $super)))
(tag $e (result (ref null $super)))
;; CHECK: (func $resume-tag (type $f)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result (ref null $next-cont))
;; CHECK-NEXT: (resume $cont (on $e $l)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null nocont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-tag
(local $cont (ref null $cont))
(drop
(block $l (result (ref null $next-cont))
;; Based on the tag, the continuation expects a $super back. Based on
;; the type we give the next continuation, we will send it a $sub back.
;; This requires $sub <: $super.
(resume $cont (on $e $l)
(local.get $cont)
)
(ref.null nocont)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func (result (ref null $sub))))
(type $f (func (result (ref null $sub))))
;; CHECK: (type $next-f (func (result (ref null $super))))
(type $next-f (func (result (ref null $super))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $next-cont (cont $next-f))
(type $next-cont (cont $next-f))
;; CHECK: (type $6 (func))
;; CHECK: (tag $e (type $6))
(tag $e)
;; CHECK: (func $resume-result (type $6)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result (ref null $next-cont))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (resume $cont (on $e $l)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null nocont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-result
(local $cont (ref null $cont))
(drop
(block $l (result (ref null $next-cont))
(drop
;; The continuation we're resuming returns a $sub. In the next type we
;; give it, it returns a $super. This requires $sub <: $super.
(resume $cont (on $e $l)
(local.get $cont)
)
)
(ref.null nocont)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func))
(type $f (func))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $4 (func (param (ref null $super))))
;; CHECK: (tag $e (type $4) (param (ref null $super)))
(tag $e (param (ref null $super)))
;; CHECK: (func $resume-throw-param (type $f)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (resume_throw $cont $e
;; CHECK-NEXT: (struct.new_default $sub)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-throw-param
(local $cont (ref null $cont))
;; This requires $sub <: $super
(resume_throw $cont $e
(struct.new $sub)
(local.get $cont)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func))
(type $f (func))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $4 (func (param (ref null $sub))))
;; CHECK: (type $5 (func (result (ref null $super) (ref null $cont))))
;; CHECK: (tag $e (type $4) (param (ref null $sub)))
(tag $e (param (ref null $sub)))
;; CHECK: (func $resume-throw-label (type $f)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (tuple.drop 2
;; CHECK-NEXT: (block $l (type $5) (result (ref null $super) (ref null $cont))
;; CHECK-NEXT: (resume_throw $cont $e (on $e $l)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-throw-label
(local $cont (ref null $cont))
(tuple.drop 2
(block $l (result (ref null $super) (ref null $cont))
;; Sending the tag parameter to the block requires $sub <: $super.
(resume_throw $cont $e (on $e $l)
(ref.null none)
(local.get $cont)
)
(unreachable)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
(rec
;; CHECK: (type $f (func (param (ref null $super) (ref null $ret-cont))))
(type $f (func (param (ref null $super) (ref null $ret-cont))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $ret-f (func))
(type $ret-f (func))
;; CHECK: (type $ret-cont (cont $ret-f))
(type $ret-cont (cont $ret-f))
)
;; CHECK: (type $6 (func))
;; CHECK: (tag $e (type $6))
(tag $e)
;; CHECK: (func $switch-param (type $6)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (switch $cont $e
;; CHECK-NEXT: (struct.new_default $sub)
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $switch-param
(local $cont (ref null $cont))
;; The continuation expects a $super, but we send it a $sub. This requires
;; $sub <: $super.
(switch $cont $e
(struct.new $sub)
(local.get $cont)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
(rec
;; CHECK: (type $f (func (param (ref null $ret-cont)) (result (ref null $sub))))
(type $f (func (param (ref null $ret-cont)) (result (ref null $sub))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $ret-f (func (result (ref null $super))))
(type $ret-f (func (result (ref null $super))))
;; CHECK: (type $ret-cont (cont $ret-f))
(type $ret-cont (cont $ret-f))
)
;; CHECK: (type $6 (func (result (ref null $super))))
;; CHECK: (type $7 (func))
;; CHECK: (tag $e (type $6) (result (ref null $super)))
(tag $e (result (ref null $super)))
;; CHECK: (func $switch-target-result (type $7)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (switch $cont $e
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $switch-target-result
(local $cont (ref null $cont))
;; The current continuation returns a $super (as indicated in the tag) and
;; the target continuation will return a $sub. This requires $sub <: $super.
(switch $cont $e
(local.get $cont)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
(rec
;; CHECK: (type $f (func (param (ref null $ret-cont)) (result (ref null $sub))))
(type $f (func (param (ref null $ret-cont)) (result (ref null $sub))))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; CHECK: (type $ret-f (func (result (ref null $super))))
(type $ret-f (func (result (ref null $super))))
;; CHECK: (type $ret-cont (cont $ret-f))
(type $ret-cont (cont $ret-f))
)
;; CHECK: (type $6 (func (result (ref null $sub))))
;; CHECK: (type $7 (func))
;; CHECK: (tag $e (type $6) (result (ref null $sub)))
(tag $e (result (ref null $sub)))
;; CHECK: (func $switch-return-result (type $7)
;; CHECK-NEXT: (local $cont (ref null $cont))
;; CHECK-NEXT: (switch $cont $e
;; CHECK-NEXT: (local.get $cont)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $switch-return-result
(local $cont (ref null $cont))
;; The current continuation returns a $sub (as indicated in the tag) and
;; after the switch it will be given the return continuation type returning
;; $super. This requires $sub <: $super.
(switch $cont $e
(local.get $cont)
)
)
)
;; Test we do not error on resume_throw_ref. It adds no subtyping constraints
;; between declared types besides those from the handlers.
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $f (func))
;; CHECK: (type $k (cont $f))
;; CHECK: (elem declare func $no_handler)
;; CHECK: (tag $e0 (type $f))
(tag $e0)
(type $f (func))
(type $k (cont $f))
;; CHECK: (func $no_handler (type $f)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $no_handler
(unreachable)
)
;; CHECK: (func $throw_unhandled_ref (type $f)
;; CHECK-NEXT: (resume_throw_ref $k
;; CHECK-NEXT: (block $h (result (ref exn))
;; CHECK-NEXT: (try_table (catch_ref $e0 $h)
;; CHECK-NEXT: (throw $e0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (cont.new $k
;; CHECK-NEXT: (ref.func $no_handler)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw_unhandled_ref
(block $h (result exnref)
(try_table (catch_ref $e0 $h) (throw $e0))
(unreachable)
)
(resume_throw_ref $k (cont.new $k (ref.func $no_handler)))
)
)
;; Test that processResumeHandlers correctly iterates handler tag parameters
;; using the inner loop variable. With two handlers where handler 1 has multiple
;; parameters, the subtyping constraint at param index 0 of handler 1 must be
;; noted (not the constraint at param index 1 repeated).
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (struct)))
(type $super (sub (struct)))
;; CHECK: (type $sub (sub $super (struct)))
(type $sub (sub $super (struct)))
;; CHECK: (type $f (func))
(type $f (func))
;; CHECK: (type $cont (cont $f))
(type $cont (cont $f))
;; First tag has 1 param; second tag has 2 params with $sub at index 0.
;; CHECK: (type $4 (func (param i32)))
;; CHECK: (type $5 (func (param (ref null $sub) i32)))
;; CHECK: (type $6 (func (result (ref null $super) i32 (ref null $cont))))
;; CHECK: (type $7 (func (result i32 (ref null $cont))))
;; CHECK: (tag $e0 (type $4) (param i32))
(tag $e0 (param i32))
;; CHECK: (tag $e1 (type $5) (param (ref null $sub) i32))
(tag $e1 (param (ref null $sub) i32))
;; CHECK: (func $resume-handler-multi-param (type $f)
;; CHECK-NEXT: (local $c (ref null $cont))
;; CHECK-NEXT: (tuple.drop 2
;; CHECK-NEXT: (block $l0 (type $7) (result i32 (ref null $cont))
;; CHECK-NEXT: (tuple.drop 3
;; CHECK-NEXT: (block $l1 (type $6) (result (ref null $super) i32 (ref null $cont))
;; CHECK-NEXT: (resume $cont (on $e0 $l0) (on $e1 $l1)
;; CHECK-NEXT: (local.get $c)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $resume-handler-multi-param
(local $c (ref null $cont))
(tuple.drop 2
(block $l0 (result i32 (ref null $cont))
(tuple.drop 3
(block $l1 (result (ref null $super) i32 (ref null $cont))
;; Handler 1 sends (ref null $sub, i32) to $l1 which expects
;; (ref null $super, i32, ref null $cont). This requires
;; $sub <: $super at param index 0 of handler 1.
(resume $cont (on $e0 $l0) (on $e1 $l1)
(local.get $c)
)
(unreachable)
)
)
(unreachable)
)
)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (func)))
(type $super (sub (func)))
;; CHECK: (type $sub (sub $super (func)))
(type $sub (sub $super (func)))
;; CHECK: (type $cont-super (sub (cont $super)))
(type $cont-super (sub (cont $super)))
;; CHECK: (type $cont-sub (sub $cont-super (cont $sub)))
(type $cont-sub (sub $cont-super (cont $sub)))
;; CHECK: (type $4 (func (param (ref $cont-sub)) (result (ref $cont-super))))
;; CHECK: (func $cont-new (type $4) (param $sub (ref $cont-sub)) (result (ref $cont-super))
;; CHECK-NEXT: (local.get $sub)
;; CHECK-NEXT: )
(func $cont-new (param $sub (ref $cont-sub)) (result (ref $cont-super))
;; This requires $cont-sub <: $cont-super and $sub <: $super.
(local.get $sub)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $super (sub (func)))
(type $super (sub (func)))
;; CHECK: (type $sub (sub (func)))
(type $sub (sub $super (func)))
;; CHECK: (type $cont-super (sub (cont $super)))
(type $cont-super (sub (cont $super)))
;; CHECK: (type $cont-sub (sub (cont $sub)))
(type $cont-sub (sub $cont-super (cont $sub)))
;; CHECK: (type $4 (func (param (ref $cont-sub)) (result (ref $cont-sub))))
;; CHECK: (func $cont-new (type $4) (param $sub (ref $cont-sub)) (result (ref $cont-sub))
;; CHECK-NEXT: (local $keepalive (ref $cont-super))
;; CHECK-NEXT: (local.get $sub)
;; CHECK-NEXT: )
(func $cont-new (param $sub (ref $cont-sub)) (result (ref $cont-sub))
(local $keepalive (ref $cont-super))
;; As above, but now we return the subtype, so there is no constraint. We
;; can unsubtype both the continuations and the funcs.
(local.get $sub)
)
)