Skip to content

Commit dbd22f8

Browse files
authored
Fix typed select pushing Type::Any instead of expected type (WebAssembly#2707)
## Summary - The typed select instruction's type checker validates operands against the expected type but forgets to set `result_type`, leaving it as `Type::Any`. - Fix adds `result_type = expected[0]` in the typed select branch. ## Details In `TypeChecker::OnSelect` (type-checker.cc), `result_type` is initialized to `Type::Any`. The untyped select path (when `expected` is empty) correctly sets `result_type = type1` on line 1012. However, the typed select path (when `expected` is non-empty) validates both operands against `expected[0]` but never assigns `result_type`. The typed select instruction (`select t`) exists specifically to provide precise type information for cases where the operand types alone are insufficient (e.g., reference types). Pushing `Type::Any` as the result type defeats this purpose and can cause downstream type checking to be less precise than intended. The fix adds `result_type = expected[0]` after the CheckType calls in the typed select branch, mirroring the pattern used in the untyped path. ## Tests - `typed-select-result-type.txt`: Verifies that typed select with funcref/externref annotations properly propagates the result type, allowing the result to be used in contexts expecting that specific reference type (e.g., `global.set` to a `funcref` global, return from an `externref` function). - `bad-typed-select-type-mismatch.txt`: Verifies that using a typed select `(result funcref)` where `externref` is expected correctly produces a type mismatch error.
1 parent 9a226dc commit dbd22f8

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

src/type-checker.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,7 @@ Result TypeChecker::OnSelect(const TypeVector& expected) {
10521052
assert(expected.size() == 1);
10531053
result |= CheckType(type1, expected[0]);
10541054
result |= CheckType(type2, expected[0]);
1055+
result_type = expected[0];
10551056
}
10561057
PrintStackIfFailed(result, "select", result_type, result_type, Type::I32);
10571058
result |= DropTypes(3);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
;;; TOOL: wat2wasm
2+
;;; ERROR: 1
3+
(module
4+
;; The result of typed select (result funcref) should be funcref,
5+
;; so using it where externref is expected should fail.
6+
(global (mut externref) (ref.null extern))
7+
(func
8+
ref.null func
9+
ref.null func
10+
i32.const 1
11+
select (result funcref)
12+
global.set 0
13+
))
14+
(;; STDERR ;;;
15+
out/test/typecheck/bad-typed-select-type-mismatch.txt:12:5: error: type mismatch in global.set, expected [externref] but got [funcref]
16+
global.set 0
17+
^^^^^^^^^^
18+
;;; STDERR ;;)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
;;; TOOL: wat2wasm
2+
(module
3+
;; Typed select with funcref should propagate the annotated type,
4+
;; allowing the result to be stored where funcref is expected.
5+
(global (mut funcref) (ref.null func))
6+
(func
7+
ref.null func
8+
ref.null func
9+
i32.const 1
10+
select (result funcref)
11+
global.set 0
12+
)
13+
;; Typed select with externref should also propagate the type.
14+
(func (param externref externref) (result externref)
15+
local.get 0
16+
local.get 1
17+
i32.const 0
18+
select (result externref)
19+
))

0 commit comments

Comments
 (0)