diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index a8c5afa8d5d..2f905482bf6 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1212,6 +1212,10 @@ void BrOn::finalize() { if (castType.isNullable()) { // Nulls take the branch, so the result is non-nullable. type = ref->type.with(NonNullable); + } else if (desc && desc->type.isNull()) { + // Cast will never be executed and the instruction will not be emitted. + // Model this with an uninhabitable cast type. + type = desc->type.with(NonNullable); } else { // Nulls do not take the branch, so the result is non-nullable only if // the input is. diff --git a/test/lit/passes/local-subtyping-desc.wast b/test/lit/passes/local-subtyping-desc.wast new file mode 100644 index 00000000000..d87bfb7ad9e --- /dev/null +++ b/test/lit/passes/local-subtyping-desc.wast @@ -0,0 +1,68 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: wasm-opt %s -all --local-subtyping -S -o - | filecheck %s + +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (sub (descriptor $desc) (struct))) + (type $struct (sub (descriptor $desc) (struct))) + ;; CHECK: (type $desc (sub (describes $struct) (struct (field i32)))) + (type $desc (sub (describes $struct) (struct (field i32)))) + ) + + ;; CHECK: (func $test (type $2) (result (ref null $struct)) + ;; CHECK-NEXT: (local $struct (ref none)) + ;; CHECK-NEXT: (block $block (result (ref none)) + ;; CHECK-NEXT: (local.set $struct + ;; CHECK-NEXT: (block ;; (replaces unreachable BrOn we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result (ref none)) + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (result (ref null $struct)) + ;; We can refine the local to (ref none). While doing so we must + ;; refinalize the br_on properly, below, to that same type (it is never + ;; executed). In more detail: + ;; + ;; * Initially, after parsing, the types of its inputs are + ;; ref=nullref, desc=(ref $desc). + ;; That refinalizes into (ref none) for the BrOn. + ;; + ;; * When we refinalize, the types of its inputs are + ;; ref=nullref, desc=(ref none). + ;; This test verifies that we fixed a bug where the BrOn was given the + ;; type of the ref, i.e., nullref - which led to an assert, as we refined + ;; (ref $struct) into nullref. That is, the code path handling a null + ;; descriptor type did not consider that it might need to emit a non- + ;; nullable type even if the ref is nullable. + ;; + (local $struct (ref $struct)) + (block $block (result (ref null $struct)) + (local.set $struct + (br_on_cast_desc_eq $block (ref null $struct) (ref null $struct) + (ref.null $struct) + (block (result (ref $desc)) + (ref.as_non_null + (ref.null none) + ) + ) + ) + ) + (unreachable) + ) + ) +) + diff --git a/test/lit/passes/monomorphize-desc.wast b/test/lit/passes/monomorphize-desc.wast index 9523bc664a8..277443a41fa 100644 --- a/test/lit/passes/monomorphize-desc.wast +++ b/test/lit/passes/monomorphize-desc.wast @@ -15,7 +15,7 @@ ;; CHECK: (type $3 (func (result anyref))) - ;; CHECK: (type $4 (func (param nullref))) + ;; CHECK: (type $4 (func (param (ref none)))) ;; CHECK: (func $target (type $2) (param $0 i32) ;; CHECK-NEXT: (nop) @@ -60,6 +60,6 @@ ) ) -;; CHECK: (func $target_2 (type $4) (param $0 nullref) +;; CHECK: (func $target_2 (type $4) (param $0 (ref none)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/vacuum-desc.wast b/test/lit/passes/vacuum-desc.wast index 37a7efdde26..ceb37fff0fd 100644 --- a/test/lit/passes/vacuum-desc.wast +++ b/test/lit/passes/vacuum-desc.wast @@ -169,7 +169,7 @@ ;; CHECK: (func $br-on-cast-null-desc (type $2) (param $ref anyref) (param $desc nullref) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $l (result anyref) + ;; CHECK-NEXT: (block $l (result (ref none)) ;; CHECK-NEXT: (block ;; (replaces unreachable BrOn we can't emit) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $ref) @@ -184,7 +184,7 @@ ;; CHECK-NEXT: ) ;; CKIIT: (func $br-on-cast-null-desc (type $2) (param $ref anyref) (param $desc nullref) ;; CKIIT-NEXT: (drop - ;; CKIIT-NEXT: (block $l (result anyref) + ;; CKIIT-NEXT: (block $l (result (ref none)) ;; CKIIT-NEXT: (block ;; (replaces unreachable BrOn we can't emit) ;; CKIIT-NEXT: (drop ;; CKIIT-NEXT: (local.get $ref)