From 5bc4b1a14c0acac3567faeb1317e4840ea179c36 Mon Sep 17 00:00:00 2001 From: Hitalo Souza Date: Fri, 26 Jun 2026 18:02:36 -0300 Subject: [PATCH] wasm: emit a clean error for scalar `?T`/`!T` returns instead of an internal ICE (#27516) --- vlib/v/gen/wasm/gen.v | 11 +++++++ .../tests/scalar_option_result_error_test.v | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 vlib/v/gen/wasm/tests/scalar_option_result_error_test.v diff --git a/vlib/v/gen/wasm/gen.v b/vlib/v/gen/wasm/gen.v index 04e77c414..64180eb9a 100644 --- a/vlib/v/gen/wasm/gen.v +++ b/vlib/v/gen/wasm/gen.v @@ -263,6 +263,17 @@ pub fn (mut g Gen) fn_decl(node ast.FnDecl) { } else { if rt.idx() != ast.void_type_idx { + // A scalar `?T`/`!T` return reaches here (it is not a MultiReturn), + // so the option/result guards below — which only run inside the + // MultiReturn arm or after the match — are dead for it. Guard before + // `get_wasm_type`, which would otherwise abort with an internal + // "unreachable type" ICE on the option/result-wrapped type. + if rt.has_flag(.option) { + g.v_error('option types are not implemented', node.return_type_pos) + } + if rt.has_flag(.result) { + g.v_error('result types are not implemented', node.return_type_pos) + } wtyp := g.get_wasm_type(rt) if g.is_param_type(rt) { paramdbg << g.dbg_type_name('__rval(0)', rt) diff --git a/vlib/v/gen/wasm/tests/scalar_option_result_error_test.v b/vlib/v/gen/wasm/tests/scalar_option_result_error_test.v new file mode 100644 index 000000000..26169a2c2 --- /dev/null +++ b/vlib/v/gen/wasm/tests/scalar_option_result_error_test.v @@ -0,0 +1,32 @@ +import os + +// A scalar `?T`/`!T` return type used to abort the wasm backend with an internal +// `get_wasm_type: unreachable type ... UnknownTypeInfo` ICE, because the +// option/result guards only ran inside the MultiReturn arm / after the type was +// already lowered. The backend must instead emit the intended, located +// "not implemented" user error. +fn compile_wasm(src string, name string) os.Result { + vexe := os.quoted_path(@VEXE) + wrkdir := os.join_path(os.vtmp_dir(), 'wasm_scalar_optres_tests') + os.mkdir_all(wrkdir) or { panic(err) } + source_path := os.join_path(wrkdir, '${name}.v') + output_path := os.join_path(wrkdir, '${name}.wasm') + os.write_file(source_path, src) or { panic(err) } + return os.execute('${vexe} -b wasm -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}') +} + +fn test_scalar_option_return_errors_cleanly() { + res := compile_wasm('fn f() ?int {\n\treturn 3\n}\n\nfn main() {\n\tx := f() or { 0 }\n\t_ = x\n}\n', + 'scalar_option') + assert res.exit_code != 0, 'expected a compile error, got: ${res.output}' + assert res.output.contains('option types are not implemented'), res.output + assert !res.output.contains('get_wasm_type: unreachable'), 'leaked the internal ICE: ${res.output}' +} + +fn test_scalar_result_return_errors_cleanly() { + res := compile_wasm('fn f() !int {\n\treturn 3\n}\n\nfn main() {\n\tx := f() or { 0 }\n\t_ = x\n}\n', + 'scalar_result') + assert res.exit_code != 0, 'expected a compile error, got: ${res.output}' + assert res.output.contains('result types are not implemented'), res.output + assert !res.output.contains('get_wasm_type: unreachable'), 'leaked the internal ICE: ${res.output}' +} -- 2.39.5