Skip to content

Commit 5bc4b1a

Browse files
authored
wasm: emit a clean error for scalar ?T/!T returns instead of an internal ICE (#27516)
1 parent 097cfd7 commit 5bc4b1a

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

vlib/v/gen/wasm/gen.v

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,17 @@ pub fn (mut g Gen) fn_decl(node ast.FnDecl) {
263263
}
264264
else {
265265
if rt.idx() != ast.void_type_idx {
266+
// A scalar `?T`/`!T` return reaches here (it is not a MultiReturn),
267+
// so the option/result guards below — which only run inside the
268+
// MultiReturn arm or after the match — are dead for it. Guard before
269+
// `get_wasm_type`, which would otherwise abort with an internal
270+
// "unreachable type" ICE on the option/result-wrapped type.
271+
if rt.has_flag(.option) {
272+
g.v_error('option types are not implemented', node.return_type_pos)
273+
}
274+
if rt.has_flag(.result) {
275+
g.v_error('result types are not implemented', node.return_type_pos)
276+
}
266277
wtyp := g.get_wasm_type(rt)
267278
if g.is_param_type(rt) {
268279
paramdbg << g.dbg_type_name('__rval(0)', rt)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import os
2+
3+
// A scalar `?T`/`!T` return type used to abort the wasm backend with an internal
4+
// `get_wasm_type: unreachable type ... UnknownTypeInfo` ICE, because the
5+
// option/result guards only ran inside the MultiReturn arm / after the type was
6+
// already lowered. The backend must instead emit the intended, located
7+
// "not implemented" user error.
8+
fn compile_wasm(src string, name string) os.Result {
9+
vexe := os.quoted_path(@VEXE)
10+
wrkdir := os.join_path(os.vtmp_dir(), 'wasm_scalar_optres_tests')
11+
os.mkdir_all(wrkdir) or { panic(err) }
12+
source_path := os.join_path(wrkdir, '${name}.v')
13+
output_path := os.join_path(wrkdir, '${name}.wasm')
14+
os.write_file(source_path, src) or { panic(err) }
15+
return os.execute('${vexe} -b wasm -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}')
16+
}
17+
18+
fn test_scalar_option_return_errors_cleanly() {
19+
res := compile_wasm('fn f() ?int {\n\treturn 3\n}\n\nfn main() {\n\tx := f() or { 0 }\n\t_ = x\n}\n',
20+
'scalar_option')
21+
assert res.exit_code != 0, 'expected a compile error, got: ${res.output}'
22+
assert res.output.contains('option types are not implemented'), res.output
23+
assert !res.output.contains('get_wasm_type: unreachable'), 'leaked the internal ICE: ${res.output}'
24+
}
25+
26+
fn test_scalar_result_return_errors_cleanly() {
27+
res := compile_wasm('fn f() !int {\n\treturn 3\n}\n\nfn main() {\n\tx := f() or { 0 }\n\t_ = x\n}\n',
28+
'scalar_result')
29+
assert res.exit_code != 0, 'expected a compile error, got: ${res.output}'
30+
assert res.output.contains('result types are not implemented'), res.output
31+
assert !res.output.contains('get_wasm_type: unreachable'), 'leaked the internal ICE: ${res.output}'
32+
}

0 commit comments

Comments
 (0)