;; 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-names --cfp --closed-world -all -S -o - | filecheck %s

;; (remove-unused-names is added to test fallthrough values without a block
;; name getting in the way)

(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))
  ;; CHECK:      (func $impossible-get (type $0)
  ;; CHECK-NEXT:  (local $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $struct)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $impossible-get (local $struct (ref null $struct))
    (drop
      ;; This type is never created, so a get is impossible, and we will trap
      ;; anyhow. So we can turn this into an unreachable (plus a drop of the
      ;; reference).
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field i64)))
  (type $struct (struct i64))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default $struct)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i64.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; The only place this type is created is with a default value, and so we
    ;; can optimize the later get into a constant (plus a drop of the ref).
    ;;
    ;; (Note that the allocated reference is dropped here, so it is not actually
    ;; used anywhere, but this pass does not attempt to trace the paths of
    ;; references escaping and being stored etc. - it just thinks at the type
    ;; level.)
    (drop
      (struct.new_default $struct)
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field f32)))
  (type $struct (struct f32))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; The only place this type is created is with a constant value, and so we
    ;; can optimize the later get into a constant (plus a drop of the ref).
    (drop
      (struct.new $struct
        (f32.const 42)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field f32)))
  (type $struct (struct f32))
  ;; CHECK:      (type $1 (func (param f32 (ref null $struct))))

  ;; CHECK:      (func $test (type $1) (param $f f32) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $f f32) (param $struct (ref null $struct))
    ;; The value given is not a constant, and so we cannot optimize.
    (drop
      (struct.new $struct
        (local.get $f)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Create in one function, get in another. The 10 should be forwarded to the
;; get.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
  )
  ;; CHECK:      (func $get (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As before, but with the order of functions reversed to check for any ordering
;; issues.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

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

  ;; CHECK:      (func $get (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )

  ;; CHECK:      (func $create (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
  )
)

;; Different values assigned in the same function, in different struct.news,
;; so we cannot optimize the struct.get away.
(module
  ;; CHECK:      (type $struct (struct (field f32)))
  (type $struct (struct f32))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (f32.const 1337)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (f32.const 42)
      )
    )
    (drop
      (struct.new $struct
        (f32.const 1337)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Different values assigned in different functions, and one is a struct.set.
(module
  ;; CHECK:      (type $struct (struct (field (mut f32))))
  (type $struct (struct (mut f32)))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

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

  ;; CHECK:      (func $create (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (f32.const 42)
      )
    )
  )
  ;; CHECK:      (func $set (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (f32.const 1337)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $set (param $struct (ref null $struct))
    (struct.set $struct 0
      (local.get $struct)
      (f32.const 1337)
    )
  )
  ;; CHECK:      (func $get (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As the last testcase, but the values happen to coincide, so we can optimize
;; the get into a constant.
(module
  ;; CHECK:      (type $struct (struct (field (mut f32))))
  (type $struct (struct (mut f32)))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

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

  ;; CHECK:      (func $create (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (f32.const 42)
      )
    )
  )
  ;; CHECK:      (func $set (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (f32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $set (param $struct (ref null $struct))
    (struct.set $struct 0
      (local.get $struct)
      (f32.const 42) ;; The last testcase had 1337 here.
    )
  )
  ;; CHECK:      (func $get (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Check that we look into the fallthrough value that is assigned.
(module
  ;; CHECK:      (type $struct (struct (field (mut f32))))
  (type $struct (struct (mut f32)))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $2 (func (param i32 (ref null $struct))))

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (block (result f32)
  ;; CHECK-NEXT:     (nop)
  ;; CHECK-NEXT:     (f32.const 42)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        ;; Fall though a 42 via a block.
        (block (result f32)
          (nop)
          (f32.const 42)
        )
      )
    )
  )
  ;; CHECK:      (func $set (type $2) (param $x i32) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (if (result f32)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (f32.const 42)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $set (param $x i32) (param $struct (ref null $struct))
    (struct.set $struct 0
      (local.get $struct)
      ;; Fall though a 42 via an if.
      (if (result f32)
        (local.get $x)
        (then
          (unreachable)
        )
        (else
          (f32.const 42)
        )
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Test a function reference instead of a number.
(module
  ;; CHECK:      (type $struct (struct (field funcref)))
  (type $struct (struct funcref))
  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (elem declare func $test)

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (ref.func $test)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref (exact $1)))
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.func $test)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (ref.func $test)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Test for unreachable creations, sets, and gets.
(module
  (type $struct (struct (mut i32)))
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (func $test (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block ;; (replaces unreachable StructNew we can't emit)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block ;; (replaces unreachable StructGet we can't emit)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block ;; (replaces unreachable StructSet we can't emit)
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    (drop
      (struct.new $struct
        (unreachable)
      )
    )
    (drop
      (struct.get $struct 0
        (unreachable)
      )
    )
    (struct.set $struct 0
      (unreachable)
      (i32.const 20)
    )
  )
)

;; Subtyping: Create a supertype and get a subtype. As we never create a
;;            subtype, the get must trap anyhow (the reference it receives can
;;            only be null in this closed world).
(module
  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))
  ;; CHECK:      (type $substruct (sub $struct (struct (field i32))))
  (type $substruct (sub $struct (struct i32)))

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

  ;; CHECK:      (type $3 (func (param (ref null $substruct))))

  ;; CHECK:      (func $create (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $substruct (ref null $substruct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $substruct)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $substruct (ref null $substruct))
    (drop
      (struct.get $substruct 0
        (local.get $substruct)
      )
    )
  )
)

;; As above, but in addition to a new of $struct also add a set. The set could
;; in principle be relevant for the get as far as this pass knows, and so we
;; will optimize the result to the only possible value. (In practice, though,
;; it will trap anyhow.)
(module
  ;; CHECK:      (type $struct (sub (struct (field (mut i32)))))
  (type $struct (sub (struct (mut i32))))
  ;; CHECK:      (type $substruct (sub $struct (struct (field (mut i32)))))
  (type $substruct (sub $struct (struct (mut i32))))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (type $3 (func (param (ref null $substruct))))

  ;; CHECK:      (func $create (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
    (struct.set $struct 0
      (local.get $struct)
      (i32.const 10)
    )
  )
  ;; CHECK:      (func $get (type $3) (param $substruct (ref null $substruct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $substruct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $substruct (ref null $substruct))
    (drop
      (struct.get $substruct 0
        (local.get $substruct)
      )
    )
  )
)

;; Subtyping: Create a subtype and get a supertype. The get must receive a
;;            reference to the subtype (we never create a supertype) and so we
;;            can optimize.
(module
  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))

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

  ;; CHECK:      (type $substruct (sub $struct (struct (field i32) (field f64))))
  (type $substruct (sub $struct (struct i32 f64)))

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $substruct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $substruct
        (i32.const 10)
        (f64.const 3.14159)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Subtyping: Create both a subtype and a supertype, with identical constants
;;            for the shared field, and get the supertype.
(module
  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $substruct (sub $struct (struct (field i32) (field f64))))
  (type $substruct (sub $struct (struct i32 f64)))

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $substruct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
    (drop
      (struct.new $substruct
        (i32.const 10)
        (f64.const 3.14159)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Subtyping: Create both a subtype and a supertype, with different constants
;;            for the shared field, preventing optimization, as a get of the
;;            supertype may receive an instance of the subtype.
;;
;; Note that this may be optimized using a ref.test, in --cfp-reftest, but not
;; in --cfp. This gives us coverage that --cfp does not do the things that
;; --cfp-reftest does (how --cfp-reftest works is tested in cfp-reftest.wast).
(module
  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $substruct (sub $struct (struct (field i32) (field f64))))
  (type $substruct (sub $struct (struct i32 f64)))

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $substruct
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
    (drop
      (struct.new $substruct
        (i32.const 20)
        (f64.const 3.14159)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Subtyping: Create both a subtype and a supertype, with different constants
;;            for the shared field, but get from the subtype. The field is
;;            shared between the types, but we only create the substruct with
;;            one value, so we can optimize.
(module

  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))

  ;; CHECK:      (type $substruct (sub $struct (struct (field i32) (field f64))))
  (type $substruct (sub $struct (struct i32 f64)))

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

  ;; CHECK:      (type $3 (func (param (ref null $substruct))))

  ;; CHECK:      (func $create (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $substruct
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
    (drop
      (struct.new $substruct
        (i32.const 20)
        (f64.const 3.14159)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $substruct (ref null $substruct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $substruct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $substruct (ref null $substruct))
    (drop
      (struct.get $substruct 0
        (local.get $substruct)
      )
    )
  )
)

;; As above, but add a set of $struct. The set prevents the optimization.
(module
  ;; CHECK:      (type $struct (sub (struct (field (mut i32)))))
  (type $struct (sub (struct (mut i32))))

  ;; CHECK:      (type $substruct (sub $struct (struct (field (mut i32)) (field f64))))
  (type $substruct (sub $struct (struct (mut i32) f64)))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (type $3 (func (param (ref null $substruct))))

  ;; CHECK:      (func $create (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $substruct
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (i32.const 10)
      )
    )
    (struct.set $struct 0
      (local.get $struct)
      (i32.const 10)
    )
    (drop
      (struct.new $substruct
        (i32.const 20)
        (f64.const 3.14159)
      )
    )
  )
  ;; CHECK:      (func $get (type $3) (param $substruct (ref null $substruct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $substruct 0
  ;; CHECK-NEXT:    (local.get $substruct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $substruct (ref null $substruct))
    (drop
      (struct.get $substruct 0
        (local.get $substruct)
      )
    )
  )
)

;; Multi-level subtyping, check that we propagate not just to the immediate
;; supertype but all the way as needed.
(module
  ;; CHECK:      (type $struct1 (sub (struct (field i32))))
  (type $struct1 (sub (struct i32)))

  ;; CHECK:      (type $struct2 (sub $struct1 (struct (field i32) (field f64))))
  (type $struct2 (sub $struct1 (struct i32 f64)))

  ;; CHECK:      (type $struct3 (sub $struct2 (struct (field i32) (field f64) (field anyref))))
  (type $struct3 (sub $struct2 (struct i32 f64 anyref)))

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

  ;; CHECK:      (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))

  ;; CHECK:      (func $create (type $3)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct3
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct3
        (i32.const 20)
        (f64.const 3.14159)
        (ref.null none)
      )
    )
  )
  ;; CHECK:      (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
    ;; Get field 0 from the $struct1. This can be optimized to a constant
    ;; since we only ever created an instance of struct3 with a constant there.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    ;; Get both fields of $struct2.
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct2 1
        (local.get $struct2)
      )
    )
    ;; Get all 3 fields of $struct3
    (drop
      (struct.get $struct3 0
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 1
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 2
        (local.get $struct3)
      )
    )
  )
)

;; Multi-level subtyping with conflicts. The even-numbered fields will get
;; different values in the sub-most type. Create the top and bottom types, but
;; not the middle one.
(module
  ;; CHECK:      (type $struct1 (sub (struct (field i32) (field i32))))
  (type $struct1 (sub (struct i32 i32)))

  ;; CHECK:      (type $struct2 (sub $struct1 (struct (field i32) (field i32) (field f64) (field f64))))
  (type $struct2 (sub $struct1 (struct i32 i32 f64 f64)))

  ;; CHECK:      (type $struct3 (sub $struct2 (struct (field i32) (field i32) (field f64) (field f64) (field anyref) (field anyref))))
  (type $struct3 (sub $struct2 (struct i32 i32 f64 f64 anyref anyref)))

  ;; CHECK:      (type $3 (func (param anyref)))

  ;; CHECK:      (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))

  ;; CHECK:      (func $create (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct3
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (i32.const 999)
  ;; CHECK-NEXT:    (f64.const 2.71828)
  ;; CHECK-NEXT:    (f64.const 9.9999999)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create (param $any anyref)
    (drop
      (struct.new $struct1
        (i32.const 10)
        (i32.const 20)
      )
    )
    (drop
      (struct.new $struct3
        (i32.const 10)
        (i32.const 999) ;; use a different value here
        (f64.const 2.71828)
        (f64.const 9.9999999)
        (ref.null any)
        (local.get $any) ;; use a non-constant value here, which can never be
                         ;; optimized.
      )
    )
  )
  ;; CHECK:      (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct1 1
  ;; CHECK-NEXT:    (local.get $struct1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 2.71828)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 9.9999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 2.71828)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 9.9999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct3 5
  ;; CHECK-NEXT:    (local.get $struct3)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
    ;; Get all the fields of all the structs.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct1 1
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct2 1
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct2 2
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct2 3
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct3 0
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 1
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 2
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 3
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 4
        (local.get $struct3)
      )
    )
    (drop
      (struct.get $struct3 5
        (local.get $struct3)
      )
    )
  )
)

;; Multi-level subtyping with a different value in the middle of the chain. We
;; can only optimize $struct3.
(module
  ;; CHECK:      (type $struct1 (sub (struct (field (mut i32)))))
  (type $struct1 (sub (struct (mut i32))))
  ;; CHECK:      (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64))))
  (type $struct2 (sub $struct1 (struct (mut i32) f64)))
  ;; CHECK:      (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref))))
  (type $struct3 (sub $struct2 (struct (mut i32) f64 anyref)))

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

  ;; CHECK:      (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))

  ;; CHECK:      (func $create (type $3)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct2
  ;; CHECK-NEXT:    (i32.const 9999)
  ;; CHECK-NEXT:    (f64.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct3
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (f64.const 0)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct1
        (i32.const 10)
      )
    )
    (drop
      (struct.new $struct2
        (i32.const 9999) ;; use a different value here
        (f64.const 0)
      )
    )
    (drop
      (struct.new $struct3
        (i32.const 10)
        (f64.const 0)
        (ref.null any)
      )
    )
  )
  ;; CHECK:      (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct1 0
  ;; CHECK-NEXT:    (local.get $struct1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct2 0
  ;; CHECK-NEXT:    (local.get $struct2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
    ;; Get field 0 in all the types.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct3 0
        (local.get $struct3)
      )
    )
  )
)

;; As above, but add not just a new of the middle class with a different value
;; but also a set. That prevents all optimizations.
(module

  ;; CHECK:      (type $struct1 (sub (struct (field (mut i32)))))
  (type $struct1 (sub (struct (mut i32))))

  ;; CHECK:      (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64))))
  (type $struct2 (sub $struct1 (struct (mut i32) f64)))

  ;; CHECK:      (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref))))
  (type $struct3 (sub $struct2 (struct (mut i32) f64 anyref)))

  ;; CHECK:      (type $3 (func (param (ref null $struct2))))

  ;; CHECK:      (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3))))

  ;; CHECK:      (func $create (type $3) (param $struct2 (ref null $struct2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct2
  ;; CHECK-NEXT:    (i32.const 9999)
  ;; CHECK-NEXT:    (f64.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct2 0
  ;; CHECK-NEXT:   (local.get $struct2)
  ;; CHECK-NEXT:   (i32.const 9999)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct3
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:    (f64.const 0)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create (param $struct2 (ref null $struct2))
    (drop
      (struct.new $struct1
        (i32.const 10)
      )
    )
    (drop
      (struct.new $struct2
        (i32.const 9999) ;; use a different value here
        (f64.const 0)
      )
    )
    (struct.set $struct2 0
      (local.get $struct2)
      (i32.const 9999) ;; use a different value here
    )
    (drop
      (struct.new $struct3
        (i32.const 10)
        (f64.const 0)
        (ref.null any)
      )
    )
  )
  ;; CHECK:      (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct1 0
  ;; CHECK-NEXT:    (local.get $struct1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct2 0
  ;; CHECK-NEXT:    (local.get $struct2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct3 0
  ;; CHECK-NEXT:    (local.get $struct3)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3))
    ;; Get field 0 in all the types.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
    (drop
      (struct.get $struct3 0
        (local.get $struct3)
      )
    )
  )
)

;; Test for a struct with multiple fields, some of which are constant and hence
;; optimizable, and some not. Also test that some have the same type.
(module
  ;; CHECK:      (type $struct (struct (field i32) (field f64) (field i32) (field f64) (field i32)))
  (type $struct (struct i32 f64 i32 f64 i32))

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

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (func $create (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.eqz
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:    (f64.abs
  ;; CHECK-NEXT:     (f64.const 2.71828)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $struct
        (i32.eqz (i32.const 10)) ;; not a constant (as far as this pass knows)
        (f64.const 3.14159)
        (i32.const 20)
        (f64.abs (f64.const 2.71828)) ;; not a constant
        (i32.const 30)
      )
    )
  )
  ;; CHECK:      (func $get (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result f64)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (f64.const 3.14159)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 3
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 1
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 2
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 3
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 4
        (local.get $struct)
      )
    )
    ;; Also test for multiple gets of the same field.
    (drop
      (struct.get $struct 4
        (local.get $struct)
      )
    )
  )
)

;; Never create A, but have a set to its field. A subtype B has no creates nor
;; sets, and the final subtype C has a create and a get. The set to A should
;; apply to it, preventing optimization.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (mut i32))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (mut i32))))

  ;; CHECK:      (type $C (sub $B (struct (field (mut i32)))))
  (type $C (sub $B (struct (mut i32))))

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

  ;; CHECK:      (type $4 (func (param (ref $A))))

  ;; CHECK:      (type $5 (func (param (ref $C))))

  ;; CHECK:      (func $create (type $3)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $C
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $create
    (drop
      (struct.new $C
        (i32.const 10)
      )
    )
  )
  ;; CHECK:      (func $set (type $4) (param $a (ref $A))
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $a)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $set (param $a (ref $A))
    (struct.set $A 0
      (local.get $a)
      (i32.const 20)
    )
  )
  ;; CHECK:      (func $get (type $5) (param $c (ref $C))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $C 0
  ;; CHECK-NEXT:    (local.get $c)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $c (ref $C))
    (drop
      (struct.get $C 0
        (local.get $c)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global i32 (i32.const 42))
  (global $global i32 (i32.const 42))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; An immutable global is the only thing written to this field, so we can
    ;; propagate the value to the struct.get and replace it with a global.get.
    (drop
      (struct.new $struct
        (global.get $global)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global (mut i32) (i32.const 42))
  (global $global (mut i32) (i32.const 42))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; As above, but the global is *not* immutable, so we cannot optimize.
    (drop
      (struct.new $struct
        (global.get $global)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field (mut i32))))
  (type $struct (struct (mut i32)))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global i32 (i32.const 42))
  (global $global i32 (i32.const 42))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (global.get $global)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (global.get $global)
      )
    )
    ;; As above, but there is another set of the field. It is the same, though,
    ;; so that is fine. Also, the struct's field is now mutable as well to allow
    ;; that, and that also does not prevent optimization.
    (struct.set $struct 0
      (local.get $struct)
      (global.get $global)
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field (mut i32))))
  (type $struct (struct (mut i32)))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global i32 (i32.const 42))
  (global $global i32 (i32.const 42))
  ;; CHECK:      (global $global-2 i32 (i32.const 1337))
  (global $global-2 i32 (i32.const 1337))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (global.get $global-2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (global.get $global)
      )
    )
    ;; As above, but set a different global, which prevents optimization.
    (struct.set $struct 0
      (local.get $struct)
      (global.get $global-2)
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; CHECK:      (type $struct (struct (field (mut i32))))
  (type $struct (struct (mut i32)))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global i32 (i32.const 42))
  (global $global i32 (i32.const 42))
  ;; CHECK:      (global $global-2 i32 (i32.const 1337))
  (global $global-2 i32 (i32.const 1337))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (i32.const 1337)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (global.get $global)
      )
    )
    ;; As above, but set a constant, which means we are mixing constants with
    ;; globals, which prevents the optimization.
    (struct.set $struct 0
      (local.get $struct)
      (i32.const 1337)
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  ;; Test a global type other than i32. Arrays of structs are a realistic case
  ;; as they are used to implement itables.

  ;; CHECK:      (type $vtable (struct (field funcref)))
  (type $vtable (struct funcref))

  ;; CHECK:      (type $itable (array (ref $vtable)))
  (type $itable (array (ref $vtable)))

  ;; CHECK:      (type $object (struct (field $itable (ref $itable))))
  (type $object (struct (field $itable (ref $itable))))

  ;; CHECK:      (type $3 (func (param (ref null $object)) (result funcref)))

  ;; CHECK:      (global $global (ref $itable) (array.new_fixed $itable 2
  ;; CHECK-NEXT:  (struct.new $vtable
  ;; CHECK-NEXT:   (ref.null nofunc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.new $vtable
  ;; CHECK-NEXT:   (ref.func $test)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global (ref $itable) (array.new_fixed $itable 2
    (struct.new $vtable
      (ref.null func)
    )
    (struct.new $vtable
      (ref.func $test)
    )
  ))

  ;; CHECK:      (func $test (type $3) (param $object (ref null $object)) (result funcref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $object
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.get $vtable 0
  ;; CHECK-NEXT:   (array.get $itable
  ;; CHECK-NEXT:    (block (result (ref $itable))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $object)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $object (ref null $object)) (result funcref)
    (drop
      (struct.new $object
        (global.get $global)
      )
    )
    ;; Realistic usage of an itable: read an item from it, then a func from
    ;; that, and return the value (all verifying that the types are correct
    ;; after optimization). Note how after optimization everything is lined up
    ;; so that precompute-propagate can infer from the global.get the specific
    ;; object the array.get is on, allowing us to emit a constant value for the
    ;; outer struct.get in principle.
    (struct.get $vtable 0
      (array.get $itable
        (struct.get $object $itable
          (local.get $object)
        )
        (i32.const 1)
      )
    )
  )
)

;; Test we handle packed fields properly.
(module
  (rec
    ;; CHECK:      (type $0 (func))

    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A_8 (struct (field i8)))
    (type $A_8 (struct (field i8)))
    ;; CHECK:       (type $A_16 (struct (field i16)))
    (type $A_16 (struct (field i16)))
    ;; CHECK:       (type $B_16 (struct (field i16)))
    (type $B_16 (struct (field i16)))
  )

  ;; CHECK:      (import "a" "b" (global $g i32))
  (import "a" "b" (global $g i32))

  ;; CHECK:      (func $test (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $A_8
  ;; CHECK-NEXT:       (i32.const 305419896)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.and
  ;; CHECK-NEXT:     (i32.const 305419896)
  ;; CHECK-NEXT:     (i32.const 255)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $A_16
  ;; CHECK-NEXT:       (i32.const 305419896)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.and
  ;; CHECK-NEXT:     (i32.const 305419896)
  ;; CHECK-NEXT:     (i32.const 65535)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $B_16
  ;; CHECK-NEXT:       (global.get $g)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.and
  ;; CHECK-NEXT:     (global.get $g)
  ;; CHECK-NEXT:     (i32.const 65535)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    ;; We can infer values here, but must add proper masks, as the inputs get
    ;; truncated during packing.
    (drop
      (struct.get_u $A_8 0
        (struct.new $A_8
          (i32.const 0x12345678)
        )
      )
    )
    (drop
      (struct.get_u $A_16 0
        (struct.new $A_16
          (i32.const 0x12345678)
        )
      )
    )
    ;; Also test reading a value from an imported global, which is an unknown
    ;; value at compile time, but which we know must be masked as well.
    (drop
      (struct.get_u $B_16 0
        (struct.new $B_16
          (global.get $g)
        )
      )
    )
  )

  ;; CHECK:      (func $test_signed (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $A_8
  ;; CHECK-NEXT:       (i32.const 305419896)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.shr_s
  ;; CHECK-NEXT:     (i32.shl
  ;; CHECK-NEXT:      (i32.const 305419896)
  ;; CHECK-NEXT:      (i32.const 24)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 24)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $A_16
  ;; CHECK-NEXT:       (i32.const 305419896)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.shr_s
  ;; CHECK-NEXT:     (i32.shl
  ;; CHECK-NEXT:      (i32.const 305419896)
  ;; CHECK-NEXT:      (i32.const 16)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 16)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (struct.new $B_16
  ;; CHECK-NEXT:       (global.get $g)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.shr_s
  ;; CHECK-NEXT:     (i32.shl
  ;; CHECK-NEXT:      (global.get $g)
  ;; CHECK-NEXT:      (i32.const 16)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 16)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test_signed
    ;; As above, but with signed gets.
    (drop
      (struct.get_s $A_8 0
        (struct.new $A_8
          (i32.const 0x12345678)
        )
      )
    )
    (drop
      (struct.get_s $A_16 0
        (struct.new $A_16
          (i32.const 0x12345678)
        )
      )
    )
    (drop
      (struct.get_s $B_16 0
        (struct.new $B_16
          (global.get $g)
        )
      )
    )
  )
)

(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))
   ;; CHECK:       (type $B (sub $A (struct (field (mut i32)))))
   (type $B (sub $A (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $2 (func (param i32) (result i32)))

  ;; CHECK:      (func $test (type $2) (param $0 i32) (result i32)
  ;; CHECK-NEXT:  (local $A (ref $A))
  ;; CHECK-NEXT:  (local $B (ref $B))
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (select (result (ref null $A))
  ;; CHECK-NEXT:    (block (result (ref null $A))
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.tee $B
  ;; CHECK-NEXT:     (struct.new $B
  ;; CHECK-NEXT:      (i32.const 20)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (select (result (ref null $A))
  ;; CHECK-NEXT:     (block (result (ref null $A))
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (struct.new $A
  ;; CHECK-NEXT:      (i32.const 10)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.get $B 0
  ;; CHECK-NEXT:   (local.get $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $0 i32) (result i32)
    (local $A (ref $A))
    (local $B (ref $B))
    ;; This set is part of a copy, as the value is a struct.get. The copy is on
    ;; $A, but the reference we operate on is an instance of $B, actually. So
    ;; the value read at the end is in fact 10. That is, this test verifies
    ;; that we track the copied value even though the copy is on $A but it
    ;; affects $B.
    (struct.set $A 0
      ;; Use selects to make sure the types reaching the set and get are not more
      ;; precise than (ref null $A). This will look like a perfect copy of
      ;; $A->$A.
      (select (result (ref null $A))
        (block (result (ref null $A))
          (ref.null none)
        )
        (local.tee $B
          (struct.new $B
            (i32.const 20)
          )
        )
        (i32.const 0)
      )
      (struct.get $A 0
        (select (result (ref null $A))
          (block (result (ref null $A))
            (ref.null none)
          )
          (struct.new $A
            (i32.const 10)
          )
          (i32.const 0)
        )
      )
    )
    ;; This should not turn into 20, since just based on values flowing to
    ;; fields we cannot infer that (the value will be 10, but CFP cannot infer
    ;; that either - that would require tracking references through locals
    ;; etc.).
    (struct.get $B 0
      (local.get $B)
    )
  )
)

(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field i32))))
   (type $A (sub (struct (field i32))))
   ;; CHECK:       (type $B (sub $A (struct (field i32))))
   (type $B (sub $A (struct (field i32))))
  )

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

  ;; CHECK:      (func $test (type $2) (param $0 i32)
  ;; CHECK-NEXT:  (local $A (ref $A))
  ;; CHECK-NEXT:  (local $B (ref $B))
  ;; CHECK-NEXT:  (local $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (local $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (local.set $A
  ;; CHECK-NEXT:   (local.tee $A-exact
  ;; CHECK-NEXT:    (struct.new $A
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $B
  ;; CHECK-NEXT:   (local.tee $B-exact
  ;; CHECK-NEXT:    (struct.new $B
  ;; CHECK-NEXT:     (i32.const 20)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $0 i32)
    (local $A (ref $A))
    (local $B (ref $B))
    (local $A-exact (ref (exact $A)))
    (local $B-exact (ref (exact $B)))
    (local.set $A
      (local.tee $A-exact
        (struct.new $A
          (i32.const 10)
        )
      )
    )
    (local.set $B
      (local.tee $B-exact
        (struct.new $B
          (i32.const 20)
        )
      )
    )
    ;; We can optimize an inexact $B, but not $A.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B)
      )
    )
    ;; We can optimize both exact references.
    (drop
      (struct.get $A 0
        (local.get $A-exact)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B-exact)
      )
    )
  )
)

(module
  ;; Same as above but now the fields are mutable.
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))
   ;; CHECK:       (type $B (sub $A (struct (field (mut i32)))))
   (type $B (sub $A (struct (field (mut i32)))))
  )

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

  ;; CHECK:      (func $test (type $2) (param $0 i32)
  ;; CHECK-NEXT:  (local $A (ref $A))
  ;; CHECK-NEXT:  (local $B (ref $B))
  ;; CHECK-NEXT:  (local $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (local $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (local.set $A
  ;; CHECK-NEXT:   (local.tee $A-exact
  ;; CHECK-NEXT:    (struct.new $A
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $B
  ;; CHECK-NEXT:   (local.tee $B-exact
  ;; CHECK-NEXT:    (struct.new $B
  ;; CHECK-NEXT:     (i32.const 20)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $0 i32)
    (local $A (ref $A))
    (local $B (ref $B))
    (local $A-exact (ref (exact $A)))
    (local $B-exact (ref (exact $B)))
    (local.set $A
      (local.tee $A-exact
        (struct.new $A
          (i32.const 10)
        )
      )
    )
    (local.set $B
      (local.tee $B-exact
        (struct.new $B
          (i32.const 20)
        )
      )
    )
    ;; We can optimize an inexact $B, but not $A.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B)
      )
    )
    ;; We should be able to optimize both exact references.
    (drop
      (struct.get $A 0
        (local.get $A-exact)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B-exact)
      )
    )
  )
)

(module
  ;; Same as above but now we add no-op sets.
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))
   ;; CHECK:       (type $B (sub $A (struct (field (mut i32)))))
   (type $B (sub $A (struct (field (mut i32)))))
  )

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

  ;; CHECK:      (func $test (type $2) (param $0 i32)
  ;; CHECK-NEXT:  (local $A (ref $A))
  ;; CHECK-NEXT:  (local $B (ref $B))
  ;; CHECK-NEXT:  (local $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (local $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (local.set $A
  ;; CHECK-NEXT:   (local.tee $A-exact
  ;; CHECK-NEXT:    (struct.new $A
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $B
  ;; CHECK-NEXT:   (local.tee $B-exact
  ;; CHECK-NEXT:    (struct.new $B
  ;; CHECK-NEXT:     (i32.const 20)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B-exact)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $0 i32)
    (local $A (ref $A))
    (local $B (ref $B))
    (local $A-exact (ref (exact $A)))
    (local $B-exact (ref (exact $B)))
    (local.set $A
      (local.tee $A-exact
        (struct.new $A
          (i32.const 10)
        )
      )
    )
    (local.set $B
      (local.tee $B-exact
        (struct.new $B
          (i32.const 20)
        )
      )
    )
    ;; No-op exact sets should not inhibit optimization.
    (struct.set $A 0
      (local.get $A-exact)
      (i32.const 10)
    )
    (struct.set $B 0
      (local.get $B-exact)
      (i32.const 20)
    )
    ;; We should be able to optimize an inexact $B, but not $A.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B)
      )
    )
    ;; We should be able to optimize both exact references TODO.
    (drop
      (struct.get $A 0
        (local.get $A-exact)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B-exact)
      )
    )
  )
)

(module
  ;; Sets to a subtype should not affect exact gets of a supertype or sibling.
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))
   ;; CHECK:       (type $B (sub $A (struct (field (mut i32)))))
   (type $B (sub $A (struct (field (mut i32)))))
   ;; CHECK:       (type $C (sub $A (struct (field (mut i32)))))
   (type $C (sub $A (struct (field (mut i32)))))
  )

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

  ;; CHECK:      (type $4 (func (param (ref $B))))

  ;; CHECK:      (type $5 (func (param (ref $A) (ref $B) (ref $C))))

  ;; CHECK:      (type $6 (func (param (ref (exact $A)) (ref (exact $B)) (ref (exact $C)))))

  ;; CHECK:      (func $news (type $3)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $C
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $news
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B
        (i32.const 20)
      )
    )
    (drop
      (struct.new $C
        (i32.const 30)
      )
    )
  )

  ;; CHECK:      (func $set-B (type $4) (param $B (ref $B))
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B)
  ;; CHECK-NEXT:   (i32.const 666)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $set-B (param $B (ref $B))
    ;; Inhibits optimizations on B and inexact A only.
    (struct.set $B 0
      (local.get $B)
      (i32.const 666)
    )
  )

  ;; CHECK:      (func $inexact-gets (type $5) (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B 0
  ;; CHECK-NEXT:    (local.get $B)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $C)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $inexact-gets (param $A (ref $A)) (param $B (ref $B)) (param $C (ref $C))
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    (drop
      (struct.get $B 0
        (local.get $B)
      )
    )
    ;; This should be optimizable.
    (drop
      (struct.get $C 0
        (local.get $C)
      )
    )
  )

  ;; CHECK:      (func $exact-gets (type $6) (param $A-exact (ref (exact $A))) (param $B-exact (ref (exact $B))) (param $C-exact (ref (exact $C)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B 0
  ;; CHECK-NEXT:    (local.get $B-exact)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $C-exact)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $exact-gets (param $A-exact (ref (exact $A)))
                    (param $B-exact (ref (exact $B)))
                    (param $C-exact (ref (exact $C)))
    (drop
      ;; This should be optimizable.
      (struct.get $A 0
        (local.get $A-exact)
      )
    )
    ;; Not optimizable.
    (drop
      (struct.get $B 0
        (local.get $B-exact)
      )
    )
    ;; This should be optimizable.
    (drop
      (struct.get $C 0
        (local.get $C-exact)
      )
    )
  )
)

;; A type with two subtypes. A copy on the parent can affect either child.
(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))

   ;; CHECK:       (type $B1 (sub $A (struct (field (mut i32)))))
   (type $B1 (sub $A (struct (field (mut i32)))))

   ;; CHECK:       (type $B2 (sub $A (struct (field (mut i32)))))
   (type $B2 (sub $A (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))

  ;; CHECK:      (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B2
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B1 0
  ;; CHECK-NEXT:    (local.get $B1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B2 0
  ;; CHECK-NEXT:    (local.get $B2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
    ;; A and B1 agree on their value in their construction.
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B1
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B2
        (i32.const 20) ;; this value is different from the others
      )
    )

    ;; Copy on $A
    (struct.set $A 0
      (local.get $A)
      (struct.get $A 0
        (local.get $A)
      )
    )

    ;; $A might read either child, so we can't infer.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    ;; $B1 should be only able to read 10, but the copy opens the possibility
    ;; of 20, so we can't optimize.
    (drop
      (struct.get $B1 0
        (local.get $B1)
      )
    )
    ;; As with $B1, the copy stops us.
    (drop
      (struct.get $B2 0
        (local.get $B2)
      )
    )
  )
)

;; As above, but now the copy is on B1.
(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))

   ;; CHECK:       (type $B1 (sub $A (struct (field (mut i32)))))
   (type $B1 (sub $A (struct (field (mut i32)))))

   ;; CHECK:       (type $B2 (sub $A (struct (field (mut i32)))))
   (type $B2 (sub $A (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))

  ;; CHECK:      (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B2
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $B1 0
  ;; CHECK-NEXT:   (local.get $B1)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B1
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B2
        (i32.const 20)
      )
    )

    ;; This changed from $A to $B1.
    (struct.set $B1 0
      (local.get $B1)
      (struct.get $B1 0
        (local.get $B1)
      )
    )

    ;; This might still read $B1 or $B2, with different values, so we can't
    ;; optimize.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    ;; The copy can only refer to $B1, so we can optimize here.
    (drop
      (struct.get $B1 0
        (local.get $B1)
      )
    )
    ;; The copy can't refer to a $B2, so we can optimize here.
    (drop
      (struct.get $B2 0
        (local.get $B2)
      )
    )
  )
)

;; As above, but now the copy is on B2.
(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $A (sub (struct (field (mut i32)))))
   (type $A (sub (struct (field (mut i32)))))

   ;; CHECK:       (type $B1 (sub $A (struct (field (mut i32)))))
   (type $B1 (sub $A (struct (field (mut i32)))))

   ;; CHECK:       (type $B2 (sub $A (struct (field (mut i32)))))
   (type $B2 (sub $A (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2))))

  ;; CHECK:      (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B1
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B2
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $B2 0
  ;; CHECK-NEXT:   (local.get $B2)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2))
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B1
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B2
        (i32.const 20)
      )
    )

    ;; This changed from $A to $B2.
    (struct.set $B2 0
      (local.get $B2)
      (struct.get $B2 0
        (local.get $B2)
      )
    )

    ;; This might still read $B1 or $B2, with different values, so we can't
    ;; optimize.
    (drop
      (struct.get $A 0
        (local.get $A)
      )
    )
    ;; The copy can't refer to a $B1, so we can optimize here.
    (drop
      (struct.get $B1 0
        (local.get $B1)
      )
    )
    ;; $B2 is copied to itself, and nothing else, so we can optimize here.
    (drop
      (struct.get $B2 0
        (local.get $B2)
      )
    )
  )
)

;; $C is created with two values for its field: a global.get, and a copy from
;; another $C, which does not expand the set of possible values. We should not
;; get confused about $B, its sibling, which is never created, and whose field
;; has an incompatible type.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $X (sub (struct)))
    (type $X (sub (struct)))
    ;; CHECK:       (type $Y (sub final $X (struct)))
    (type $Y (sub final $X (struct)))
    ;; CHECK:       (type $Z (sub final $X (struct)))
    (type $Z (sub final $X (struct)))

    ;; CHECK:       (type $A (sub (struct (field (ref null $X)))))
    (type $A (sub (struct (field (ref null $X)))))
    ;; CHECK:       (type $B (sub final $A (struct (field (ref null $Y)))))
    (type $B (sub final $A (struct (field (ref null $Y)))))
    ;; CHECK:       (type $C (sub final $A (struct (field (ref null $Z)))))
    (type $C (sub final $A (struct (field (ref null $Z)))))
  )

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

  ;; CHECK:      (type $7 (func (param (ref null $C))))

  ;; CHECK:      (type $8 (func (param (ref null $A)) (result (ref null $X))))

  ;; CHECK:      (type $9 (func (param (ref null $B)) (result (ref null $Y))))

  ;; CHECK:      (global $global (ref null $Z) (struct.new_default $Z))
  (global $global (ref null $Z) (struct.new_default $Z))

  ;; CHECK:      (func $new (type $6)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $C
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $new
    (drop
      (struct.new $C
        (global.get $global)
      )
    )
  )

  ;; CHECK:      (func $copy (type $7) (param $C (ref null $C))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $C
  ;; CHECK-NEXT:    (block (result (ref null $Z))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $C)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $C (ref null $C))
    ;; The struct.get here can be optimized to a global.get.
    (drop
      (struct.new $C
        (struct.get $C 0
          (local.get $C)
        )
      )
    )
  )

  ;; CHECK:      (func $get-A (type $8) (param $A (ref null $A)) (result (ref null $X))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.get $global)
  ;; CHECK-NEXT: )
  (func $get-A (param $A (ref null $A)) (result (ref null $X))
    ;; The struct.get here can be optimized to a global.get. While we never
    ;; create an $A, a $C might be referred to by an $A reference, and the
    ;; global.get is the only possible value.
    (struct.get $A 0
      (local.get $A)
    )
  )

  ;; CHECK:      (func $get-B (type $9) (param $B (ref null $B)) (result (ref null $Y))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $get-B (param $B (ref null $B)) (result (ref null $Y))
    ;; This should not be optimized to a global.get: no $B is created, and we
    ;; cannot refer to anything that is actually created. If we mistakenly
    ;; thought this field can contain the global.get (as we do for the parent
    ;; $A) then we would error here: $B's field contains $Y, but the global is
    ;; is of a sibling type $Z. Instead, we can add an unreachable here, as no
    ;; valid value is possible.
    (struct.get $B 0
      (local.get $B)
    )
  )
)

;; Atomic accesses require special handling
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $shared (shared (struct (field (mut i32)))))
    (type $shared (shared (struct (mut i32))))
    ;; CHECK:       (type $unwritten (shared (struct (field (mut i32)))))
    (type $unwritten (shared (struct (mut i32))))
  )

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

  ;; CHECK:      (type $3 (func (param (ref $shared))))

  ;; CHECK:      (type $4 (func (param (ref $unwritten))))

  ;; CHECK:      (func $init (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default $shared)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init
    (drop
      (struct.new_default $shared)
    )
  )

  ;; CHECK:      (func $gets (type $3) (param $0 (ref $shared))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.get acqrel $shared 0
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.get $shared 0
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $gets (param (ref $shared))
    (drop
      (struct.get $shared 0
        (local.get 0)
      )
    )
    (drop
      ;; This can be optimzied in principle, but our analysis cannot yet prove
      ;; there is no synchronization. TODO.
      (struct.atomic.get acqrel $shared 0
        (local.get 0)
      )
    )
    (drop
      ;; Same as above.
      (struct.atomic.get $shared 0
        (local.get 0)
      )
    )
  )

  ;; CHECK:      (func $traps (type $4) (param $0 (ref $unwritten))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $traps (param (ref $unwritten))
    ;; This are all optimizable because they are known to trap. No fences are
    ;; necessary.
    (drop
      (struct.get $unwritten 0
        (local.get 0)
      )
    )
    (drop
      (struct.atomic.get acqrel $unwritten 0
        (local.get 0)
      )
    )
    (drop
      (struct.atomic.get $unwritten 0
        (local.get 0)
      )
    )
  )
)

(module
  ;; CHECK:      (type $A (struct (field (mut i32))))
  (type $A (struct (field (mut i32))))

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

  ;; CHECK:      (func $test (type $1)
  ;; CHECK-NEXT:  (local $A.exact (ref (exact $A)))
  ;; CHECK-NEXT:  (local.set $A.exact
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A.exact)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $A.exact)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    (local $A.exact (ref (exact $A)))
    (local.set $A.exact
      (struct.new $A
        (i32.const 10)
      )
    )
    ;; This set prevents us from optimizing. We should not be confused by the
    ;; exactness of the ref.
    (struct.set $A 0
      (local.get $A.exact)
      (i32.const 20)
    )
    (drop
      (struct.get $A 0
        (local.get $A.exact)
      )
    )
  )
)
