diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 519049456cb..3a458772467 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -189,9 +189,15 @@ struct FunctionDirectizer : public WalkerPass> { // Everything looks good! auto name = std::get(info).target; - replaceCurrent( - Builder(*getModule()) - .makeCall(name, operands, original->type, original->isReturn)); + auto results = getModule()->getFunction(name)->getResults(); + replaceCurrent(Builder(*getModule()) + .makeCall(name, operands, results, original->isReturn)); + + // When we call a function of a subtype of the call_indirect's call type, we + // may be refining results. + if (results != original->type) { + changedTypes = true; + } } }; diff --git a/test/lit/passes/directize-gc.wast b/test/lit/passes/directize-gc.wast index fcf5c98116f..20f5ac1f9a4 100644 --- a/test/lit/passes/directize-gc.wast +++ b/test/lit/passes/directize-gc.wast @@ -112,3 +112,40 @@ ) ) +;; call_indirect using the supertype. The direct call has a more refined type, +;; which we must update the IR to. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $super (sub (func (result (ref any))))) + (type $super (sub (func (result (ref any))))) + ;; CHECK: (type $sub (sub $super (func (result (ref none))))) + (type $sub (sub $super (func (result (ref none))))) + ) + + ;; CHECK: (table $table 42 funcref) + (table $table 42 funcref) + ;; CHECK: (elem $elem (i32.const 0) $sub) + (elem $elem (i32.const 0) $sub) + + ;; CHECK: (func $super (type $super) (result (ref any)) + ;; CHECK-NEXT: (block $show-type (result (ref none)) + ;; CHECK-NEXT: (call $sub) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $super (type $super) (result (ref any)) + (block $show-type (result (ref any)) + (call_indirect $table (type $super) + (i32.const 0) + ) + ) + ) + + ;; CHECK: (func $sub (type $sub) (result (ref none)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $sub (type $sub) (result (ref none)) + (unreachable) + ) +) +