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

;; Check that exact casts are only added when custom descriptors are enabled.

;; RUN: foreach %s %t wasm-opt -all                              --gufa-cast-all -S -o - \
;; RUN:     | filecheck %s

;; RUN: foreach %s %t wasm-opt -all --disable-custom-descriptors --gufa-cast-all -S -o - \
;; RUN:     | filecheck %s --check-prefix=NO_CD

(module
  ;; CHECK:      (type $foo (struct))
  ;; NO_CD:      (type $foo (struct))
  (type $foo (struct))

  ;; CHECK:      (import "" "" (global $g (ref (exact $foo))))
  ;; NO_CD:      (import "" "" (global $g (ref (exact $foo))))
  (import "" "" (global $g (ref (exact $foo))))

  ;; CHECK:      (func $callee (type $1) (result (ref $foo))
  ;; CHECK-NEXT:  (global.get $g)
  ;; CHECK-NEXT: )
  ;; NO_CD:      (func $callee (type $1) (result (ref $foo))
  ;; NO_CD-NEXT:  (global.get $g)
  ;; NO_CD-NEXT: )
  (func $callee (result (ref $foo))
    (global.get $g)
  )

  ;; CHECK:      (func $caller (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref (exact $foo)))
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.cast (ref (exact $foo))
  ;; CHECK-NEXT:      (call $callee)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $g)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; NO_CD:      (func $caller (type $2)
  ;; NO_CD-NEXT:  (drop
  ;; NO_CD-NEXT:   (block (result (ref (exact $foo)))
  ;; NO_CD-NEXT:    (drop
  ;; NO_CD-NEXT:     (call $callee)
  ;; NO_CD-NEXT:    )
  ;; NO_CD-NEXT:    (global.get $g)
  ;; NO_CD-NEXT:   )
  ;; NO_CD-NEXT:  )
  ;; NO_CD-NEXT: )
  (func $caller
    (drop
      (call $callee)
    )
  )
)

(module
 ;; CHECK:      (type $foo (struct))
 ;; NO_CD:      (type $foo (struct))
 (type $foo (struct))

 ;; CHECK:      (import "" "a" (global $exact-a (ref (exact $foo))))
 ;; NO_CD:      (import "" "a" (global $exact-a (ref (exact $foo))))
 (import "" "a" (global $exact-a (ref (exact $foo))))
 ;; CHECK:      (import "" "b" (global $exact-b (ref (exact $foo))))
 ;; NO_CD:      (import "" "b" (global $exact-b (ref (exact $foo))))
 (import "" "b" (global $exact-b (ref (exact $foo))))

 ;; CHECK:      (global $g (mut (ref $foo)) (global.get $exact-a))
 ;; NO_CD:      (global $g (mut (ref $foo)) (global.get $exact-a))
 (global $g (mut (ref $foo)) (global.get $exact-a))

 ;; CHECK:      (func $set (type $1)
 ;; CHECK-NEXT:  (global.set $g
 ;; CHECK-NEXT:   (global.get $exact-b)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $set (type $1)
 ;; NO_CD-NEXT:  (global.set $g
 ;; NO_CD-NEXT:   (global.get $exact-b)
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $set
  ;; $g can now hold two different exact $foo references.
  (global.set $g
   (global.get $exact-b)
  )
 )

 ;; CHECK:      (func $get (type $2) (result (ref $foo))
 ;; CHECK-NEXT:  (ref.cast (ref (exact $foo))
 ;; CHECK-NEXT:   (ref.cast (ref (exact $foo))
 ;; CHECK-NEXT:    (global.get $g)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $get (type $2) (result (ref $foo))
 ;; NO_CD-NEXT:  (ref.cast (ref $foo)
 ;; NO_CD-NEXT:   (global.get $g)
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $get (result (ref $foo))
  ;; We can only refine this cast target to be exact if custom descriptors are
  ;; allowed.
  (ref.cast (ref $foo)
   (global.get $g)
  )
 )
)

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

 ;; CHECK:      (import "" "" (global $exact (ref (exact $foo))))
 ;; NO_CD:      (import "" "" (global $exact (ref (exact $foo))))
 (import "" "" (global $exact (ref (exact $foo))))

 ;; CHECK:      (func $get (type $1) (result i32)
 ;; CHECK-NEXT:  (struct.get $foo 0
 ;; CHECK-NEXT:   (select (result (ref null (exact $foo)))
 ;; CHECK-NEXT:    (global.get $exact)
 ;; CHECK-NEXT:    (ref.null none)
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $get (type $1) (result i32)
 ;; NO_CD-NEXT:  (struct.get $foo 0
 ;; NO_CD-NEXT:   (select (result (ref null (exact $foo)))
 ;; NO_CD-NEXT:    (global.get $exact)
 ;; NO_CD-NEXT:    (ref.null none)
 ;; NO_CD-NEXT:    (i32.const 0)
 ;; NO_CD-NEXT:   )
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $get (result i32)
  ;; Regression test for a bug where exactness was not preserved when combining
  ;; nullness into cone types, resulting in a later assertion failure on the
  ;; StructGet.
  (struct.get $foo 0
   (select (result (ref null (exact $foo)))
    (global.get $exact)
    (ref.null none)
    (i32.const 0)
   )
  )
 )
)

(module
 ;; CHECK:      (type $super (sub (struct)))
 ;; NO_CD:      (type $super (sub (struct)))
 (type $super (sub (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:      (import "" "a" (global $exact-a (ref (exact $super))))
 ;; NO_CD:      (import "" "a" (global $exact-a (ref (exact $super))))
 (import "" "a" (global $exact-a (ref (exact $super))))
 ;; CHECK:      (import "" "b" (global $exact-b (ref (exact $super))))
 ;; NO_CD:      (import "" "b" (global $exact-b (ref (exact $super))))
 (import "" "b" (global $exact-b (ref (exact $super))))
 ;; CHECK:      (import "" "x" (global $x i32))
 ;; NO_CD:      (import "" "x" (global $x i32))
 (import "" "x" (global $x i32))

 ;; CHECK:      (func $get-exact (type $1) (result (ref $super))
 ;; CHECK-NEXT:  (select (result (ref (exact $super)))
 ;; CHECK-NEXT:   (global.get $exact-a)
 ;; CHECK-NEXT:   (global.get $exact-b)
 ;; CHECK-NEXT:   (global.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $get-exact (type $1) (result (ref $super))
 ;; NO_CD-NEXT:  (select (result (ref (exact $super)))
 ;; NO_CD-NEXT:   (global.get $exact-a)
 ;; NO_CD-NEXT:   (global.get $exact-b)
 ;; NO_CD-NEXT:   (global.get $x)
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $get-exact (result (ref $super))
  (select (result (ref (exact $super)))
   (global.get $exact-a)
   (global.get $exact-b)
   (global.get $x)
  )
 )

 ;; CHECK:      (func $cast-sub (type $2)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block
 ;; CHECK-NEXT:    (drop
 ;; CHECK-NEXT:     (ref.cast (ref (exact $super))
 ;; CHECK-NEXT:      (call $get-exact)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $cast-sub (type $2)
 ;; NO_CD-NEXT:  (drop
 ;; NO_CD-NEXT:   (block
 ;; NO_CD-NEXT:    (drop
 ;; NO_CD-NEXT:     (call $get-exact)
 ;; NO_CD-NEXT:    )
 ;; NO_CD-NEXT:    (unreachable)
 ;; NO_CD-NEXT:   )
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $cast-sub
  (drop
   ;; We should be able to infer that this cast will not succeed and insert an
   ;; unreachable after it.
   (ref.cast (ref $sub)
    (call $get-exact)
   )
  )
 )
)

(module
 ;; CHECK:      (type $foo (sub (struct (field i32))))
 ;; NO_CD:      (type $foo (sub (struct (field i32))))
 (type $foo (sub (struct (field i32))))

 ;; CHECK:      (import "" "" (global $null-exact (ref null (exact $foo))))
 ;; NO_CD:      (import "" "" (global $null-exact (ref null (exact $foo))))
 (import "" "" (global $null-exact (ref null (exact $foo))))

 ;; CHECK:      (func $as-non-null (type $1) (result i32)
 ;; CHECK-NEXT:  (struct.get $foo 0
 ;; CHECK-NEXT:   (ref.as_non_null
 ;; CHECK-NEXT:    (global.get $null-exact)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_CD:      (func $as-non-null (type $1) (result i32)
 ;; NO_CD-NEXT:  (struct.get $foo 0
 ;; NO_CD-NEXT:   (ref.as_non_null
 ;; NO_CD-NEXT:    (global.get $null-exact)
 ;; NO_CD-NEXT:   )
 ;; NO_CD-NEXT:  )
 ;; NO_CD-NEXT: )
 (func $as-non-null (result i32)
  (struct.get $foo 0
   ;; Regression test for an assertion failure when the intersection here
   ;; dropped exactness as well as nullness.
   (ref.as_non_null
    (global.get $null-exact)
   )
  )
 )
)
