| (module | |
| ;; Two structs, A and B, each of which has a subtype. | |
| (rec | |
| (type $A (sub (struct))) | |
| (type $A2 (sub $A (struct))) | |
| (type $B (sub (struct))) | |
| (type $B2 (sub $B(struct))) | |
| ) | |
| ;; Two imports, one which will be referenced. | |
| (import "module" "base" (func $import (param i32 anyref) (result eqref))) | |
| (import "module" "base" (func $import-reffed (param i32 anyref) (result eqref))) | |
| ;; Two exports, one which will be referenced. | |
| (func $export (export "export") (param $0 i32) (param $1 anyref) (result eqref) | |
| ;; Add the refs. | |
| (drop | |
| (ref.func $import-reffed) | |
| ) | |
| (drop | |
| (ref.func $export-reffed) | |
| ) | |
| ;; Call the imports. | |
| (drop | |
| (call $import | |
| (i32.const 10) | |
| ;; Send $A. We can refine the anyref to $A or $A2 (but not $B or $B2). | |
| (struct.new $A) | |
| ) | |
| ) | |
| (drop | |
| (call $import-reffed | |
| (i32.const 20) | |
| (struct.new $A) | |
| ) | |
| ) | |
| ;; Return $B. We can refine the eqref to $B or $B2 (but not $A or $A2). | |
| (struct.new $B) | |
| ) | |
| (func $export-reffed (export "export-reffed") (param $0 i32) (param $1 anyref) (result eqref) | |
| (struct.new $A) | |
| ) | |
| ;; An export without a ref.func but that has a tail call, which also prevents | |
| ;; us from refining its results. The called function and the caller both | |
| ;; return nullref initially, and each might be refined to a conflicting type, | |
| ;; if we are not careful here. | |
| (func $tail-called (export "tail-called") (result nullref) | |
| (ref.null $A) | |
| ) | |
| (func $tail-caller (export "tail-caller") (param $x i32) (result nullref) | |
| (if | |
| (local.get $x) | |
| (then | |
| (return | |
| (ref.null $B) | |
| ) | |
| ) | |
| ) | |
| (return_call $tail-called) | |
| ) | |
| ) |