;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --closed-world -all -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements                -all -S -o - | filecheck %s --check-prefix OPEN_WORLD

;; Test both open world (default) and closed world. In a closed world we can do
;; more with function refs, as we assume nothing calls them on the outside, so
;; if no calls exist to the right type, the function is not reached.

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A-super (sub (func)))
    ;; OPEN_WORLD:      (rec
    ;; OPEN_WORLD-NEXT:  (type $A-super (sub (func)))
    (type $A-super (sub (func)))

    ;; CHECK:       (type $A (sub $A-super (func)))
    ;; OPEN_WORLD:       (type $A (sub $A-super (func)))
    (type $A (sub $A-super (func)))

    ;; CHECK:       (type $A-sub (sub $A (func)))
    ;; OPEN_WORLD:       (type $A-sub (sub $A (func)))
    (type $A-sub (sub $A (func)))

    ;; CHECK:       (type $B (func))
    ;; OPEN_WORLD:       (type $B (func))
    (type $B (func))
  )

  ;; CHECK:      (type $4 (func))

  ;; CHECK:      (elem declare func $target-A $target-A-sub $target-A-super $target-B)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $4)
  ;; CHECK-NEXT:  (local $A (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-sub)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-super)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block ;; (replaces unreachable CallRef we can't emit)
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (type $4 (func))

  ;; OPEN_WORLD:      (elem declare func $target-A $target-A-sub $target-A-super $target-B)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $4)
  ;; OPEN_WORLD-NEXT:  (local $A (ref null $A))
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-sub)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-super)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-B)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (block ;; (replaces unreachable CallRef we can't emit)
  ;; OPEN_WORLD-NEXT:   (drop
  ;; OPEN_WORLD-NEXT:    (unreachable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:   (unreachable)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    (local $A (ref null $A))
    ;; This export has some RefFuncs, and one CallRef.
    (drop
      (ref.func $target-A)
    )
    (drop
      (ref.func $target-A-sub)
    )
    (drop
      (ref.func $target-A-super)
    )
    (drop
      (ref.func $target-B)
    )
    (call_ref $A
      (local.get $A)
    )
    ;; Verify that we do not crash on an unreachable call_ref, which has no
    ;; heap type for us to analyze.
    (call_ref $A
      (unreachable)
    )
  )

  ;; CHECK:      (func $target-A (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A (type $A)
    ;; This function is reachable from the export "foo": there is a RefFunc and
    ;; a CallRef for it there.
  )

  (func $target-A-noref (type $A)
    ;; This function is not reachable. We have a CallRef of the right type, but
    ;; no RefFunc.
  )

  ;; CHECK:      (func $target-A-sub (type $A-sub)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-sub (type $A-sub)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-sub (type $A-sub)
    ;; This function is reachable because we have a CallRef of a supertype ($A).
  )

  ;; CHECK:      (func $target-A-super (type $A-super)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-super (type $A-super)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-super (type $A-super)
    ;; This function is not reachable. We have a CallRef of a subtype ($A), but
    ;; that is not enough.
  )

  ;; CHECK:      (func $target-B (type $B)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-B (type $B)
  ;; OPEN_WORLD-NEXT: )
  (func $target-B (type $B)
    ;; This function is not reachable. We have a RefFunc in "foo" but no
    ;; suitable CallRef.
    ;;
    ;; Note that we cannot remove the function, as the RefFunc must refer to
    ;; something in order to validate. But we can clear out the body of this
    ;; function with an unreachable.
    ;;
    ;; As mentioned above, in an open world we cannot optimize here, so the
    ;; function body will remain empty as a nop, and not turn into an
    ;; unreachable.
  )
)

;; As above, but reverse the order inside $foo, so we see the CallRef first.
(module
  ;; CHECK:      (type $A (func))
  ;; OPEN_WORLD:      (type $A (func))
  (type $A (func))
  (type $B (func))

  ;; CHECK:      (elem declare func $target-A)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $A)
  ;; CHECK-NEXT:  (local $A (ref null $A))
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $target-A)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $A)
  ;; OPEN_WORLD-NEXT:  (local $A (ref null $A))
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    (local $A (ref null $A))
    (call_ref $A
      (local.get $A)
    )
    (drop
      (ref.func $target-A)
    )
  )

  ;; CHECK:      (func $target-A (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A (type $A)
    ;; This function is reachable.
  )

  (func $target-A-noref (type $A)
    ;; This function is not reachable.
  )
)

;; As above, but interleave CallRefs with RefFuncs.
(module
  ;; CHECK:      (type $A (func))
  ;; OPEN_WORLD:      (type $A (func))
  (type $A (func))
  (type $B (func))

  ;; CHECK:      (elem declare func $target-A-1 $target-A-2)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $A)
  ;; CHECK-NEXT:  (local $A (ref null $A))
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $target-A-1 $target-A-2)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $A)
  ;; OPEN_WORLD-NEXT:  (local $A (ref null $A))
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-1)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-2)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    (local $A (ref null $A))
    (call_ref $A
      (local.get $A)
    )
    (drop
      (ref.func $target-A-1)
    )
    (call_ref $A
      (local.get $A)
    )
    (drop
      (ref.func $target-A-2)
    )
  )

  ;; WORLD_OPEN-NEXT: )
  ;; CHECK:      (func $target-A-1 (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-1 (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-1 (type $A)
    ;; This function is reachable.
  )

  ;; CHECK:      (func $target-A-2 (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-2 (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-2 (type $A)
    ;; This function is reachable.
  )

  (func $target-A-3 (type $A)
    ;; This function is not reachable.
  )
)

;; As above, with the order reversed inside $foo. The results should be the
;; same.
(module
  ;; CHECK:      (type $A (func))
  ;; OPEN_WORLD:      (type $A (func))
  (type $A (func))
  (type $B (func))

  ;; CHECK:      (elem declare func $target-A-1 $target-A-2)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $A)
  ;; CHECK-NEXT:  (local $A (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-A-2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $target-A-1 $target-A-2)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $A)
  ;; OPEN_WORLD-NEXT:  (local $A (ref null $A))
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-1)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-A-2)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $A
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    (local $A (ref null $A))
    (drop
      (ref.func $target-A-1)
    )
    (call_ref $A
      (local.get $A)
    )
    (drop
      (ref.func $target-A-2)
    )
    (call_ref $A
      (local.get $A)
    )
  )

  ;; CHECK:      (func $target-A-1 (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-1 (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-1 (type $A)
    ;; This function is reachable.
  )

  ;; CHECK:      (func $target-A-2 (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-A-2 (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-A-2 (type $A)
    ;; This function is reachable.
  )

  (func $target-A-3 (type $A)
    ;; This function is not reachable.
  )
)

;; call_indirect can reach things in the table, or that are written to the table
;; during runtime.
(module
  ;; CHECK:      (type $none_=>_none (func))
  ;; OPEN_WORLD:      (type $none_=>_none (func))
  (type $none_=>_none (func))

  ;; CHECK:      (table $table 22 funcref)
  ;; OPEN_WORLD:      (table $table 22 funcref)
  (table $table 22 funcref)

  ;; CHECK:      (elem declare func $func)

  ;; CHECK:      (export "run" (func $run))

  ;; CHECK:      (func $run (type $none_=>_none)
  ;; CHECK-NEXT:  (table.set $table
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (ref.func $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $table (type $none_=>_none)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $func)

  ;; OPEN_WORLD:      (export "run" (func $run))

  ;; OPEN_WORLD:      (func $run (type $none_=>_none)
  ;; OPEN_WORLD-NEXT:  (table.set $table
  ;; OPEN_WORLD-NEXT:   (i32.const 0)
  ;; OPEN_WORLD-NEXT:   (ref.func $func)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_indirect $table (type $none_=>_none)
  ;; OPEN_WORLD-NEXT:   (i32.const 0)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $run (export "run")
    ;; Set something in the table, and call it.
    (table.set $table
      (i32.const 0)
      (ref.func $func)
    )
    (call_indirect $table (type $none_=>_none)
      (i32.const 0)
    )
  )

  ;; CHECK:      (func $func (type $none_=>_none)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $func (type $none_=>_none)
  ;; OPEN_WORLD-NEXT:  (nop)
  ;; OPEN_WORLD-NEXT: )
  (func $func (type $none_=>_none)
    ;; This function will be called indirectly from |run|, so it is reachable
    ;; and this should not be turned into |unreachable|.
    (nop)
  )
)

;; The call.without.effects intrinsic does a call to the reference given to it,
;; but for now other imports do not (until we add a flag for closed-world).
(module
  ;; CHECK:      (type $A (func))
  ;; OPEN_WORLD:      (type $A (func))
  (type $A (func))

  (import "binaryen-intrinsics" "call.without.effects"
    (func $call-without-effects (param funcref)))

  (import "other" "import"
    (func $other-import (param funcref)))

  ;; CHECK:      (type $1 (func (param funcref)))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param funcref)))

  ;; CHECK:      (import "other" "import" (func $other-import (type $1) (param funcref)))

  ;; CHECK:      (elem declare func $target-drop $target-keep)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $A)
  ;; CHECK-NEXT:  (call $call-without-effects
  ;; CHECK-NEXT:   (ref.func $target-keep)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $other-import
  ;; CHECK-NEXT:   (ref.func $target-drop)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (type $1 (func (param funcref)))

  ;; OPEN_WORLD:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param funcref)))

  ;; OPEN_WORLD:      (import "other" "import" (func $other-import (type $1) (param funcref)))

  ;; OPEN_WORLD:      (elem declare func $target-drop $target-keep)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $A)
  ;; OPEN_WORLD-NEXT:  (call $call-without-effects
  ;; OPEN_WORLD-NEXT:   (ref.func $target-keep)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call $other-import
  ;; OPEN_WORLD-NEXT:   (ref.func $target-drop)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    ;; Calling the intrinsic with a reference is considered a call of the
    ;; reference, so we will not remove $target-keep.
    (call $call-without-effects
      (ref.func $target-keep)
    )
    ;; The other import is not enough to keep $target-drop alive.
    (call $other-import
      (ref.func $target-drop)
    )
  )

  ;; CHECK:      (func $target-keep (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-keep (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-keep (type $A)
  )

  ;; CHECK:      (func $target-drop (type $A)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-drop (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-drop (type $A)
    ;; In a closed world we can turn this body into unreachable.
  )
)

;; As above, but now the call to the intrinsic does not let us see the exact
;; function being called.
(module
  ;; CHECK:      (type $A (func))
  ;; OPEN_WORLD:      (type $A (func))
  (type $A (func))

  (import "binaryen-intrinsics" "call.without.effects"
    (func $call-without-effects (param funcref)))

  (import "other" "import"
    (func $other-import (param funcref)))

  ;; CHECK:      (type $1 (func (param funcref)))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param funcref)))

  ;; CHECK:      (import "other" "import" (func $other-import (type $1) (param funcref)))

  ;; CHECK:      (elem declare func $target-keep $target-keep-2)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $A)
  ;; CHECK-NEXT:  (local $A (ref null $A))
  ;; CHECK-NEXT:  (call $call-without-effects
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $target-keep)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $other-import
  ;; CHECK-NEXT:   (ref.func $target-keep-2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (type $1 (func (param funcref)))

  ;; OPEN_WORLD:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param funcref)))

  ;; OPEN_WORLD:      (import "other" "import" (func $other-import (type $1) (param funcref)))

  ;; OPEN_WORLD:      (elem declare func $target-keep $target-keep-2)

  ;; OPEN_WORLD:      (export "foo" (func $foo))

  ;; OPEN_WORLD:      (func $foo (type $A)
  ;; OPEN_WORLD-NEXT:  (local $A (ref null $A))
  ;; OPEN_WORLD-NEXT:  (call $call-without-effects
  ;; OPEN_WORLD-NEXT:   (local.get $A)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (ref.func $target-keep)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call $other-import
  ;; OPEN_WORLD-NEXT:   (ref.func $target-keep-2)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $foo (export "foo")
    (local $A (ref null $A))
    ;; Call the intrinsic without a RefFunc. All we infer here is the type,
    ;; which means we must assume anything with type $A (and a reference) can be
    ;; called, which will keep alive both $target-keep and $target-keep-2
    (call $call-without-effects
      (local.get $A)
    )
    (drop
      (ref.func $target-keep)
    )
    (call $other-import
      (ref.func $target-keep-2)
    )
  )

  ;; CHECK:      (func $target-keep (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-keep (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-keep (type $A)
  )

  ;; CHECK:      (func $target-keep-2 (type $A)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $target-keep-2 (type $A)
  ;; OPEN_WORLD-NEXT: )
  (func $target-keep-2 (type $A)
  )
)

;; Test reachability of struct fields in globals. Only fields that have actual
;; reads need to be processed.
(module
  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  ;; CHECK:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  ;; OPEN_WORLD:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))

  ;; CHECK:      (global $vtable (ref $vtable) (struct.new $vtable
  ;; CHECK-NEXT:  (ref.func $a)
  ;; CHECK-NEXT:  (ref.func $b)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $vtable (ref $vtable) (struct.new $vtable
  ;; OPEN_WORLD-NEXT:  (ref.func $a)
  ;; OPEN_WORLD-NEXT:  (ref.func $b)
  ;; OPEN_WORLD-NEXT: ))
  (global $vtable (ref $vtable) (struct.new $vtable
    (ref.func $a)
    (ref.func $b)
  ))

  (global $vtable-2 (ref $vtable) (struct.new $vtable
    (ref.func $c)
    (ref.func $d)
  ))

  ;; CHECK:      (export "export" (func $export))

  ;; CHECK:      (func $export (type $void)
  ;; CHECK-NEXT:  (call $b)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (export "export" (func $export))

  ;; OPEN_WORLD:      (func $export (type $void)
  ;; OPEN_WORLD-NEXT:  (call $b)
  ;; OPEN_WORLD-NEXT: )
  (func $export (export "export")
    ;; Call $b but not $a or $c
    (call $b)
  )

  ;; CHECK:      (func $a (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $a (type $void)
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 0
  ;; OPEN_WORLD-NEXT:    (global.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $a (type $void)
    ;; $a calls field #0 in the vtable.
    ;;
    ;; Even though $a is in the vtable, it is dead, since the vtable is alive
    ;; but there is no live read of field #0 - the only read is in here, which
    ;; is basically an unreachable cycle that we can collect. We can empty out
    ;; this function since it is dead, but we cannot remove it entirely due to
    ;; the ref in the vtable.
    ;;
    ;; (In open world, however, we cannot do this, as we must assume reads of
    ;; struct fields can occur outside of our view. That is, the vtable could be
    ;; sent somewhere that reads field #0, which would make $a live.)
    (call_ref $void
      (struct.get $vtable 0
        (global.get $vtable)
      )
    )
  )

  ;; CHECK:      (func $b (type $void)
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (struct.get $vtable 1
  ;; CHECK-NEXT:    (global.get $vtable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $b (type $void)
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 1
  ;; OPEN_WORLD-NEXT:    (global.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $b (type $void)
    ;; $b calls field #1 in the vtable.
    ;;
    ;; As $b is called from the export, this function is not dead.
    (call_ref $void
      (struct.get $vtable 1
        (global.get $vtable)
      )
    )
  )

  (func $c (type $void)
    ;; $c is parallel to $a, but using vtable-2, which has no other references,
    ;; so this is dead like $a, and can be removed entirely.
    (call_ref $void
      (struct.get $vtable 0
        (global.get $vtable-2)
      )
    )
  )

  (func $d (type $void)
    ;; $d is parallel to $b, but using vtable-2, which has no other references.
    ;; This is dead, even though the struct type + index have a use (due to the
    ;; other vtable) - there is no use of vtable-2 (except from unreachable
    ;; places like here), so this cannot be reached.
    (call_ref $void
      (struct.get $vtable 0
        (global.get $vtable-2)
      )
    )
  )
)

;; Test struct.news not in globals.
(module
  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  ;; CHECK:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  ;; OPEN_WORLD:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))

  ;; CHECK:      (type $struct (sub (struct (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)))))
  ;; OPEN_WORLD:      (type $struct (sub (struct (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)))))
  (type $struct (sub (struct (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)))))

  ;; CHECK:      (global $vtable (ref $vtable) (struct.new $vtable
  ;; CHECK-NEXT:  (ref.func $a)
  ;; CHECK-NEXT:  (ref.func $b)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $vtable (ref $vtable) (struct.new $vtable
  ;; OPEN_WORLD-NEXT:  (ref.func $a)
  ;; OPEN_WORLD-NEXT:  (ref.func $b)
  ;; OPEN_WORLD-NEXT: ))
  (global $vtable (ref $vtable) (struct.new $vtable
    (ref.func $a)
    (ref.func $b)
  ))

  ;; CHECK:      (elem declare func $c $d $e $f $g $h $void)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (local $ref (ref $struct))
  ;; CHECK-NEXT:  (local $vtable (ref $vtable))
  ;; CHECK-NEXT:  (local.set $ref
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $vtable)
  ;; CHECK-NEXT:    (struct.new $vtable
  ;; CHECK-NEXT:     (ref.func $c)
  ;; CHECK-NEXT:     (ref.func $d)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.tee $vtable
  ;; CHECK-NEXT:     (struct.new $vtable
  ;; CHECK-NEXT:      (ref.func $e)
  ;; CHECK-NEXT:      (ref.func $f)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (struct.new $vtable
  ;; CHECK-NEXT:     (ref.func $g)
  ;; CHECK-NEXT:     (ref.func $h)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block ;; (replaces unreachable StructNew we can't emit)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 1
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 2
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $vtable 1
  ;; CHECK-NEXT:    (local.get $vtable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $void)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $c $d $e $f $g $h $void)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (local $ref (ref $struct))
  ;; OPEN_WORLD-NEXT:  (local $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (local.set $ref
  ;; OPEN_WORLD-NEXT:   (struct.new $struct
  ;; OPEN_WORLD-NEXT:    (global.get $vtable)
  ;; OPEN_WORLD-NEXT:    (struct.new $vtable
  ;; OPEN_WORLD-NEXT:     (ref.func $c)
  ;; OPEN_WORLD-NEXT:     (ref.func $d)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:    (local.tee $vtable
  ;; OPEN_WORLD-NEXT:     (struct.new $vtable
  ;; OPEN_WORLD-NEXT:      (ref.func $e)
  ;; OPEN_WORLD-NEXT:      (ref.func $f)
  ;; OPEN_WORLD-NEXT:     )
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:    (struct.new $vtable
  ;; OPEN_WORLD-NEXT:     (ref.func $g)
  ;; OPEN_WORLD-NEXT:     (ref.func $h)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (block ;; (replaces unreachable StructNew we can't emit)
  ;; OPEN_WORLD-NEXT:    (drop
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:    (drop
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:    (unreachable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $struct 0
  ;; OPEN_WORLD-NEXT:    (local.get $ref)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $struct 1
  ;; OPEN_WORLD-NEXT:    (local.get $ref)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $struct 2
  ;; OPEN_WORLD-NEXT:    (local.get $ref)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 1
  ;; OPEN_WORLD-NEXT:    (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $void)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    (local $ref (ref $struct))
    (local $vtable (ref $vtable))

    (local.set $ref
      (struct.new $struct
        ;; Init one field using the global vtable.
        (global.get $vtable)
        ;; Init another field using a vtable we create here - a nested
        ;; struct.new inside this one.
        (struct.new $vtable
          (ref.func $c)
          (ref.func $d)
        )
        ;; Another nested one, but now there is a side effect. Everything here
        ;; is considered to escape due to that.
        (local.tee $vtable
          (struct.new $vtable
            (ref.func $e)
            (ref.func $f)
          )
        )
        ;; Another nested one. This field will not be read.
        (struct.new $vtable
          (ref.func $g)
          (ref.func $h)
        )
      )
    )

    ;; Test that we do not assert on an unreachable struct.new.
    (drop
      (struct.new $vtable
        (unreachable)
        (unreachable)
      )
    )

    ;; Read from all fields of $struct except for the last.
    (drop
      (struct.get $struct 0
        (local.get $ref)
      )
    )
    (drop
      (struct.get $struct 1
        (local.get $ref)
      )
    )
    (drop
      (struct.get $struct 2
        (local.get $ref)
      )
    )

    ;; Read from field #1 of the vtable type, but not #0.
    (drop
      (struct.get $vtable 1
        (local.get $vtable)
      )
    )

    ;; Call something of type void so we don't eliminate them all instantly.
    (call_ref $void
      (ref.func $void)
    )
  )

  ;; CHECK:      (func $void (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $void (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $a (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $a (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $a (type $void)
    ;; This is unreachable (in closed world) since a reference to it only exists
    ;; in field #0 of the vtable type, which is never read from.
  )

  ;; CHECK:      (func $b (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $b (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $b (type $void)
    ;; This is reachable. It is in field #1, which is read, and the global
    ;; vtable is also read, and the type $void is call_reffed.
  )

  ;; CHECK:      (func $c (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $c (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $c (type $void)
    ;; Like $a, this is unreachable. That it is in a nested struct.new, and not
    ;; in a global, does not matter.
  )

  ;; CHECK:      (func $d (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $d (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $d (type $void)
    ;; Like $b, this is reachable. That it is in a nested struct.new, and not
    ;; in a global, does not matter.
  )

  ;; CHECK:      (func $e (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $e (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $e (type $void)
    ;; Side effects on the struct field are not enough to make this reachable:
    ;; there is a tee on the struct.new we are in, but field #0 is still not
    ;; read from the relevant struct.
  )

  ;; CHECK:      (func $f (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f (type $void)
    ;; Like $b, this is reachable (the tee does not matter).
  )

  ;; CHECK:      (func $g (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $g (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $g (type $void)
    ;; This is in a struct written to a field that is never read in $struct, so
    ;; it is unreachable.
  )

  ;; CHECK:      (func $h (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $h (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $h (type $void)
    ;; This is in a struct written to a field that is never read in $struct, so
    ;; it is unreachable.
  )
)

;; Test side effects causing a value to "leak."
(module
  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  ;; CHECK:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  ;; OPEN_WORLD:      (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))
  (type $vtable (sub (struct (field (ref $void)) (field (ref $void)))))

  ;; CHECK:      (elem declare func $a $b $void)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (local $vtable (ref $vtable))
  ;; CHECK-NEXT:  (local $void (ref $void))
  ;; CHECK-NEXT:  (local.set $vtable
  ;; CHECK-NEXT:   (struct.new $vtable
  ;; CHECK-NEXT:    (ref.func $a)
  ;; CHECK-NEXT:    (local.tee $void
  ;; CHECK-NEXT:     (ref.func $b)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $void)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $a $b $void)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (local $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (local $void (ref $void))
  ;; OPEN_WORLD-NEXT:  (local.set $vtable
  ;; OPEN_WORLD-NEXT:   (struct.new $vtable
  ;; OPEN_WORLD-NEXT:    (ref.func $a)
  ;; OPEN_WORLD-NEXT:    (local.tee $void
  ;; OPEN_WORLD-NEXT:     (ref.func $b)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $void)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    (local $vtable (ref $vtable))
    (local $void (ref $void))

    ;; Init one field using a tee, and one normally.
    (local.set $vtable
      (struct.new $vtable
        (ref.func $a)
        (local.tee $void
          (ref.func $b)
        )
      )
    )

    ;; Call the type so it is reachable.
    (call_ref $void
      (ref.func $void)
    )
  )

  ;; CHECK:      (func $void (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $void (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $a (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $a (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $a (type $void)
    ;; This is unreachable (in closed world) because we have no reads from the
    ;; struct field it is written in.
  )

  ;; CHECK:      (func $b (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $b (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $b (type $void)
    ;; The local.tee makes this reachable: the value is not known to only reside
    ;; in the struct field, so we must assume it can be used even if the struct
    ;; field is not.
  )
)

;; Cycles.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $vtable-func (func (param (ref $vtable))))
    ;; OPEN_WORLD:      (rec
    ;; OPEN_WORLD-NEXT:  (type $vtable-func (func (param (ref $vtable))))
    (type $vtable-func (func (param (ref $vtable))))
    ;; CHECK:       (type $vtable (sub (struct (field (ref $vtable-func)) (field (ref $vtable-func)))))
    ;; OPEN_WORLD:       (type $vtable (sub (struct (field (ref $vtable-func)) (field (ref $vtable-func)))))
    (type $vtable (sub (struct (field (ref $vtable-func)) (field (ref $vtable-func)))))
  )

  ;; CHECK:      (type $2 (func))

  ;; CHECK:      (elem declare func $a $b $c $d)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $2)
  ;; CHECK-NEXT:  (call_ref $vtable-func
  ;; CHECK-NEXT:   (struct.new $vtable
  ;; CHECK-NEXT:    (ref.func $a)
  ;; CHECK-NEXT:    (ref.func $b)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (struct.get $vtable 0
  ;; CHECK-NEXT:    (struct.new $vtable
  ;; CHECK-NEXT:     (ref.func $a)
  ;; CHECK-NEXT:     (ref.func $b)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $vtable 0
  ;; CHECK-NEXT:    (struct.new $vtable
  ;; CHECK-NEXT:     (ref.func $c)
  ;; CHECK-NEXT:     (ref.func $d)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (type $2 (func))

  ;; OPEN_WORLD:      (elem declare func $a $b $c $d)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $2)
  ;; OPEN_WORLD-NEXT:  (call_ref $vtable-func
  ;; OPEN_WORLD-NEXT:   (struct.new $vtable
  ;; OPEN_WORLD-NEXT:    (ref.func $a)
  ;; OPEN_WORLD-NEXT:    (ref.func $b)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 0
  ;; OPEN_WORLD-NEXT:    (struct.new $vtable
  ;; OPEN_WORLD-NEXT:     (ref.func $a)
  ;; OPEN_WORLD-NEXT:     (ref.func $b)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 0
  ;; OPEN_WORLD-NEXT:    (struct.new $vtable
  ;; OPEN_WORLD-NEXT:     (ref.func $c)
  ;; OPEN_WORLD-NEXT:     (ref.func $d)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    ;; Read field 0, and call it.
    (call_ref $vtable-func
      (struct.new $vtable
        (ref.func $a)
        (ref.func $b)
      )
      (struct.get $vtable 0
        ;; Duplicate the first vtable.
        (struct.new $vtable
          (ref.func $a)
          (ref.func $b)
        )
      )
    )

    ;; Again, read field #0. No need to call it here (the call before makes the
    ;; type used).
    (drop
      (struct.get $vtable 0
        ;; Make a new vtable with new funcs.
        (struct.new $vtable
          (ref.func $c)
          (ref.func $d)
        )
      )
    )
  )

  ;; CHECK:      (func $a (type $vtable-func) (param $vtable (ref $vtable))
  ;; CHECK-NEXT:  (call_ref $vtable-func
  ;; CHECK-NEXT:   (local.get $vtable)
  ;; CHECK-NEXT:   (struct.get $vtable 0
  ;; CHECK-NEXT:    (local.get $vtable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $a (type $vtable-func) (param $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (call_ref $vtable-func
  ;; OPEN_WORLD-NEXT:   (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 0
  ;; OPEN_WORLD-NEXT:    (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $a (type $vtable-func) (param $vtable (ref $vtable))
    ;; $a calls $a or $c (using field #0).
    ;; $a is reached from $func, so it is reachable.
    (call_ref $vtable-func
      (local.get $vtable)
      (struct.get $vtable 0
        (local.get $vtable)
      )
    )
  )

  ;; CHECK:      (func $b (type $vtable-func) (param $vtable (ref $vtable))
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $b (type $vtable-func) (param $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (call_ref $vtable-func
  ;; OPEN_WORLD-NEXT:   (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 1
  ;; OPEN_WORLD-NEXT:    (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $b (type $vtable-func) (param $vtable (ref $vtable))
    ;; $b calls $b or $d (using field #1).
    ;; But $b is not reached from $func, so it remains unreachable in closed
    ;; world.
    (call_ref $vtable-func
      (local.get $vtable)
      (struct.get $vtable 1
        (local.get $vtable)
      )
    )
  )

  ;; CHECK:      (func $c (type $vtable-func) (param $vtable (ref $vtable))
  ;; CHECK-NEXT:  (call_ref $vtable-func
  ;; CHECK-NEXT:   (local.get $vtable)
  ;; CHECK-NEXT:   (struct.get $vtable 0
  ;; CHECK-NEXT:    (local.get $vtable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $c (type $vtable-func) (param $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (call_ref $vtable-func
  ;; OPEN_WORLD-NEXT:   (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 0
  ;; OPEN_WORLD-NEXT:    (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $c (type $vtable-func) (param $vtable (ref $vtable))
    ;; $c forms a cycle with $a.
    (call_ref $vtable-func
      (local.get $vtable)
      (struct.get $vtable 0
        (local.get $vtable)
      )
    )
  )

  ;; CHECK:      (func $d (type $vtable-func) (param $vtable (ref $vtable))
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $d (type $vtable-func) (param $vtable (ref $vtable))
  ;; OPEN_WORLD-NEXT:  (call_ref $vtable-func
  ;; OPEN_WORLD-NEXT:   (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   (struct.get $vtable 1
  ;; OPEN_WORLD-NEXT:    (local.get $vtable)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $d (type $vtable-func) (param $vtable (ref $vtable))
    ;; $d forms a cycle with $b.
    (call_ref $vtable-func
      (local.get $vtable)
      (struct.get $vtable 1
        (local.get $vtable)
      )
    )
  )
)

;; Subtyping of struct reads.
(module
  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  ;; CHECK:      (type $struct (sub (struct (field funcref))))
  ;; OPEN_WORLD:      (type $struct (sub (struct (field funcref))))
  (type $struct (sub (struct (field funcref))))

  ;; CHECK:      (type $substruct (sub $struct (struct (field funcref))))
  ;; OPEN_WORLD:      (type $substruct (sub $struct (struct (field funcref))))
  (type $substruct (sub $struct (struct (field funcref))))

  ;; CHECK:      (type $subsubstruct (sub $substruct (struct (field funcref))))
  ;; OPEN_WORLD:      (type $subsubstruct (sub $substruct (struct (field funcref))))
  (type $subsubstruct (sub $substruct (struct (field funcref))))

  ;; CHECK:      (global $g (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (ref.func $f)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $g (ref $struct) (struct.new $struct
  ;; OPEN_WORLD-NEXT:  (ref.func $f)
  ;; OPEN_WORLD-NEXT: ))
  (global $g (ref $struct) (struct.new $struct
    (ref.func $f)
  ))

  ;; CHECK:      (global $subg (ref $substruct) (struct.new $substruct
  ;; CHECK-NEXT:  (ref.func $subf)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $subg (ref $substruct) (struct.new $substruct
  ;; OPEN_WORLD-NEXT:  (ref.func $subf)
  ;; OPEN_WORLD-NEXT: ))
  (global $subg (ref $substruct) (struct.new $substruct
    (ref.func $subf)
  ))

  ;; CHECK:      (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct
  ;; CHECK-NEXT:  (ref.func $subsubf)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct
  ;; OPEN_WORLD-NEXT:  (ref.func $subsubf)
  ;; OPEN_WORLD-NEXT: ))
  (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct
    (ref.func $subsubf)
  ))

  ;; CHECK:      (elem declare func $func)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $g)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $subg)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $subsubg)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $substruct 0
  ;; CHECK-NEXT:    (block (result (ref $substruct))
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $func)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (global.get $g)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (global.get $subg)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (global.get $subsubg)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $substruct 0
  ;; OPEN_WORLD-NEXT:    (block (result (ref $substruct))
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $func)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    ;; Refer to the globals.
    (drop
      (global.get $g)
    )
    (drop
      (global.get $subg)
    )
    (drop
      (global.get $subsubg)
    )

    ;; Read from $substruct's field, but not its super or subtypes.
    (drop
      (struct.get $substruct 0
        (block (result (ref $substruct))
          (unreachable)
        )
      )
    )

    ;; Call the function type to allow functions to be used.
    (call_ref $void
      (ref.func $func)
    )
  )

  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $f (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f (type $void)
    ;; This is unreachable in closed world. The global it is in has a reference
    ;; but the struct there has no reads of its field.
  )

  ;; CHECK:      (func $subf (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $subf (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $subf (type $void)
    ;; There is a read of $substruct's field, which makes this reachable.
  )

  ;; CHECK:      (func $subsubf (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $subsubf (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $subsubf (type $void)
    ;; There is a read of $substruct's field, which may read from any subtype,
    ;; which makes this reachable.
  )
)

;; Test references to global function references.
(module
  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  ;; CHECK:      (type $A (struct (field funcref)))
  ;; OPEN_WORLD:      (type $A (struct (field funcref)))
  (type $A (struct (field funcref)))

  ;; CHECK:      (global $g1 (ref func) (ref.func $f1))
  ;; OPEN_WORLD:      (global $g1 (ref func) (ref.func $f1))
  (global $g1 (ref func) (ref.func $f1))

  ;; CHECK:      (global $g2 (ref func) (ref.func $f2))
  ;; OPEN_WORLD:      (global $g2 (ref func) (ref.func $f2))
  (global $g2 (ref func) (ref.func $f2))

  ;; CHECK:      (elem declare func $func)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $g1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (global.get $g2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $func)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (global.get $g1)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.new $A
  ;; OPEN_WORLD-NEXT:    (global.get $g2)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $func)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    ;; Refer to $g1 directly.
    (drop
      (global.get $g1)
    )
    ;; Refer to $g2 from a struct field that is never read.
    (drop
      (struct.new $A
        (global.get $g2)
      )
    )

    ;; Call the function type to allow functions to be used.
    (call_ref $void
      (ref.func $func)
    )
  )

  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $f1 (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f1 (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f1 (type $void)
    ;; The global containing this function's reference is used.
  )

  ;; CHECK:      (func $f2 (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f2 (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f2 (type $void)
    ;; This is unreachable in closed world as the global is referred to from a
    ;; struct field that is never read from.
  )
)

;; As above, but now the globals are struct.news.
(module
  ;; CHECK:      (type $A (struct (field funcref)))

  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $A (struct (field funcref)))

  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  (type $A (struct (field funcref)))

  ;; CHECK:      (type $B (struct (field (ref $A))))
  ;; OPEN_WORLD:      (type $B (struct (field (ref $A))))
  (type $B (struct (field (ref $A))))

  ;; CHECK:      (global $g (ref $A) (struct.new $A
  ;; CHECK-NEXT:  (ref.func $f)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $g (ref $A) (struct.new $A
  ;; OPEN_WORLD-NEXT:  (ref.func $f)
  ;; OPEN_WORLD-NEXT: ))
  (global $g (ref $A) (struct.new $A
    (ref.func $f)
  ))

  ;; CHECK:      (elem declare func $func)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (global.get $g)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (block (result (ref $A))
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $func)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.new $B
  ;; OPEN_WORLD-NEXT:    (global.get $g)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $func)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $A 0
  ;; OPEN_WORLD-NEXT:    (block (result (ref $A))
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    ;; Refer to $g from a struct field that is never read.
    (drop
      (struct.new $B
        (global.get $g)
      )
    )

    ;; Call the function type to allow functions to be used.
    (call_ref $void
      (ref.func $func)
    )

    ;; Read $A's field.
    (drop
      (struct.get $A 0
        (block (result (ref $A))
          (unreachable)
        )
      )
    )
  )

  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $f (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f (type $void)
    ;; This is unreachable in closed world since $B's field is not read, so the
    ;; global it is in is only referenced and not used.
  )
)

;; As above, but read $B's field. Now $f is reachable.
(module
  ;; CHECK:      (type $A (struct (field funcref)))

  ;; CHECK:      (type $void (func))
  ;; OPEN_WORLD:      (type $A (struct (field funcref)))

  ;; OPEN_WORLD:      (type $void (func))
  (type $void (func))

  (type $A (struct (field funcref)))

  ;; CHECK:      (type $B (struct (field (ref $A))))
  ;; OPEN_WORLD:      (type $B (struct (field (ref $A))))
  (type $B (struct (field (ref $A))))

  ;; CHECK:      (global $g (ref $A) (struct.new $A
  ;; CHECK-NEXT:  (ref.func $f)
  ;; CHECK-NEXT: ))
  ;; OPEN_WORLD:      (global $g (ref $A) (struct.new $A
  ;; OPEN_WORLD-NEXT:  (ref.func $f)
  ;; OPEN_WORLD-NEXT: ))
  (global $g (ref $A) (struct.new $A
    (ref.func $f)
  ))

  ;; CHECK:      (elem declare func $func)

  ;; CHECK:      (export "func" (func $func))

  ;; CHECK:      (func $func (type $void)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (global.get $g)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (block (result (ref $A))
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B 0
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $func)

  ;; OPEN_WORLD:      (export "func" (func $func))

  ;; OPEN_WORLD:      (func $func (type $void)
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.new $B
  ;; OPEN_WORLD-NEXT:    (global.get $g)
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (call_ref $void
  ;; OPEN_WORLD-NEXT:   (ref.func $func)
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $A 0
  ;; OPEN_WORLD-NEXT:    (block (result (ref $A))
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.get $B 0
  ;; OPEN_WORLD-NEXT:    (block (result (ref $B))
  ;; OPEN_WORLD-NEXT:     (unreachable)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $func (export "func")
    (drop
      (struct.new $B
        (global.get $g)
      )
    )

    (call_ref $void
      (ref.func $func)
    )

    (drop
      (struct.get $A 0
        (block (result (ref $A))
          (unreachable)
        )
      )
    )

    ;; The change in this testcase is to read $B's field.
    (drop
      (struct.get $B 0
        (block (result (ref $B))
          (unreachable)
        )
      )
    )
  )

  (func $void (type $void)
    ;; Helper function. This is reached via a call_ref.
  )

  ;; CHECK:      (func $f (type $void)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $f (type $void)
  ;; OPEN_WORLD-NEXT: )
  (func $f (type $void)
  )
)

;; The call.without.effects intrinsic reports itself as having no side effects.
;; We do still need to consider the target as being called, however, even if it
;; is in a struct field.
(module
  ;; CHECK:      (type $0 (func (param funcref) (result i32)))

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $A (struct (field i32)))
  ;; OPEN_WORLD:      (type $0 (func (param funcref) (result i32)))

  ;; OPEN_WORLD:      (type $1 (func))

  ;; OPEN_WORLD:      (type $A (struct (field i32)))
  (type $A (struct (field i32)))

  ;; CHECK:      (type $3 (func (result i32)))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (type $0) (param funcref) (result i32)))
  ;; OPEN_WORLD:      (type $3 (func (result i32)))

  ;; OPEN_WORLD:      (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (type $0) (param funcref) (result i32)))
  (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (param funcref) (result i32)))

  ;; CHECK:      (elem declare func $getter)

  ;; CHECK:      (export "main" (func $main))

  ;; CHECK:      (func $main (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (call $call.without.effects
  ;; CHECK-NEXT:     (ref.func $getter)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (elem declare func $getter)

  ;; OPEN_WORLD:      (export "main" (func $main))

  ;; OPEN_WORLD:      (func $main (type $1)
  ;; OPEN_WORLD-NEXT:  (drop
  ;; OPEN_WORLD-NEXT:   (struct.new $A
  ;; OPEN_WORLD-NEXT:    (call $call.without.effects
  ;; OPEN_WORLD-NEXT:     (ref.func $getter)
  ;; OPEN_WORLD-NEXT:    )
  ;; OPEN_WORLD-NEXT:   )
  ;; OPEN_WORLD-NEXT:  )
  ;; OPEN_WORLD-NEXT: )
  (func $main (export "main")
    (drop
      (struct.new $A
        (call $call.without.effects
          (ref.func $getter)
        )
      )
    )
  )

  ;; CHECK:      (func $getter (type $3) (result i32)
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  ;; OPEN_WORLD:      (func $getter (type $3) (result i32)
  ;; OPEN_WORLD-NEXT:  (i32.const 42)
  ;; OPEN_WORLD-NEXT: )
  (func $getter (result i32)
    ;; This function body should not be turned into an unreachable. It is
    ;; reached from $main, even though the call is marked as not having effects.
    (i32.const 42)
  )
)
