;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; RUN: foreach %s %t wasm-opt --closed-world --cfp-reftest -all -S -o - | filecheck %s

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref $A))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A (ref $A))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A (ref $A))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set inexact A.
    (struct.set $A 0
      (local.get $A)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    ;; Copy to exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We won't be able to optimize because the set of inexact A could go to
    ;; anything.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref $A))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A (ref $A))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A (ref $A))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set inexact A.
    (struct.set $A 0
      (local.get $A)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $B (ref $B))
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $B (ref $B))
    ;; Copy to inexact B.
    (struct.set $B 0
      (local.get $B)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We won't be able to optimize because the set of inexact A could go to
    ;; anything.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref $A))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A (ref $A))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A (ref $A))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set inexact A.
    (struct.set $A 0
      (local.get $A)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B-exact)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $B-exact (ref (exact $B)))
    ;; Copy to exact B.
    (struct.set $B 0
      (local.get $B-exact)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We won't be able to optimize because the set of inexact A could go to
    ;; anything.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $A)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $A (ref $A))
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $A (ref $A))
    ;; Copy to inexact A.
    (struct.set $A 0
      (local.get $A)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We won't be able to optimize because the copy to inexact A could go to
    ;; anything.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $A)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $B (ref $B))
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $B (ref $B))
    ;; Copy to inexact B.
    (struct.set $B 0
      (local.get $B)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:   (ref.test (ref $B)
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $A)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We can optimize! Only B will have the copied value.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $A)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $init (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (struct.set $B 0
  ;; CHECK-NEXT:   (local.get $B-exact)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $B-exact (ref (exact $B)))
    ;; Copy to exact B.
    (struct.set $B 0
      (local.get $B-exact)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:   (ref.test (ref $B)
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $A)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We can optimize! Only B will have the copied value.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $B)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; 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: )
  (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact B.
    (struct.set $B 0
      (local.get $B-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    ;; Copy to exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (struct.get $other 0
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:   (ref.test (ref $B)
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $A)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; Switch the copy and set destinations from the previous case. We can still
    ;; optimize!
    (struct.get $A 0
      (local.get $A)
    )
  )
)


(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 $other (sub (struct (field (mut i32)) (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)) (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $B)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; 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: )
  (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact B.
    (struct.set $B 0
      (local.get $B-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $A-exact (ref (exact $A)))
  ;; CHECK-NEXT:  (struct.set $other 1
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A-exact)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $A-exact (ref (exact $A)))
    ;; Copy to another field and from there to exact A.
    (struct.set $other 1
      (local.get $other)
      (struct.get $other 0
        (local.get $other)
      )
    )
    ;; Copy to exact A.
    (struct.set $A 0
      (local.get $A-exact)
      (struct.get $other 1
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:   (ref.test (ref $B)
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $A)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We can still optimize after doing two copies.
    (struct.get $A 0
      (local.get $A)
    )
  )
)

(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 $other (sub (struct (field (mut i32)) (field (mut i32)))))
    (type $other (sub (struct (field (mut i32)) (field (mut i32)))))
  )

  ;; CHECK:      (type $3 (func (param (ref $other) (ref (exact $B)))))

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

  ;; CHECK:      (type $5 (func (param (ref $A)) (result i32)))

  ;; CHECK:      (func $init (type $3) (param $other (ref $other)) (param $B-exact (ref (exact $B)))
  ;; CHECK-NEXT:  (struct.set $other 0
  ;; CHECK-NEXT:   (local.get $other)
  ;; 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: )
  (func $init (param $other (ref $other)) (param $B-exact (ref (exact $B)))
    (struct.set $other 0
      (local.get $other)
      (i32.const 10)
    )
    ;; Set exact B.
    (struct.set $B 0
      (local.get $B-exact)
      (i32.const 20)
    )
  )

  ;; CHECK:      (func $copy (type $4) (param $other (ref $other)) (param $A (ref $A))
  ;; CHECK-NEXT:  (struct.set $other 1
  ;; CHECK-NEXT:   (local.get $other)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $other)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $copy (param $other (ref $other)) (param $A (ref $A))
    ;; Copy to another field and from there to inexact A.
    (struct.set $other 1
      (local.get $other)
      (struct.get $other 0
        (local.get $other)
      )
    )
    ;; Copy to A.
    (struct.set $A 0
      (local.get $A)
      (struct.get $other 1
        (local.get $other)
      )
    )
  )

  ;; CHECK:      (func $get (type $5) (param $A (ref $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get (param $A (ref $A)) (result i32)
    ;; We cannot optimize because the copy to inexact A could write to anything.
    (struct.get $A 0
      (local.get $A)
    )
  )
)
