diff --git a/test/other/test_emit_tsd_multivalue.c b/test/other/test_emit_tsd_multivalue.c new file mode 100644 index 0000000000000..b4729720e41ab --- /dev/null +++ b/test/other/test_emit_tsd_multivalue.c @@ -0,0 +1,14 @@ +#include + +struct Pair { int a; int b; }; + +// Returning a small struct by value with the experimental multi-value ABI +// causes clang to emit a wasm function with two return values (i32, i32). +// This mirrors the shape wasm-bindgen produces for e.g. `pub fn foo() -> String` +// (a ptr+len pair) on the wasm32-unknown-emscripten target. +EMSCRIPTEN_KEEPALIVE struct Pair make_pair(int a, int b) { + struct Pair p = {a, b}; + return p; +} + +int main() {} diff --git a/test/test_other.py b/test/test_other.py index f77aa3e902129..72f79f9175db7 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -3803,6 +3803,21 @@ def test_emit_tsd_wasm_only(self): expected = 'Wasm only output is not compatible with --emit-tsd' self.assert_fail([EMCC, test_file('other/test_emit_tsd.c'), '--emit-tsd', 'test_emit_tsd_wasm_only.d.ts', '-o', 'out.wasm'], expected) + def test_emit_tsd_multivalue(self): + # A wasm export that returns multiple values (e.g. wasm-bindgen's + # ptr+len pair for `pub fn foo() -> String`) used to crash --emit-tsd + # with `AssertionError: One return type only supported`. It should + # now surface as a TypeScript tuple return type. + self.run_process([EMCC, test_file('other/test_emit_tsd_multivalue.c'), + '-mmultivalue', '-Xclang', '-target-abi', '-Xclang', 'experimental-mv', + '--emit-tsd', 'test_emit_tsd_multivalue.d.ts', + '-sMODULARIZE', '-sEXPORT_ES6', + '-sEXPORT_KEEPALIVE', + '-o', 'test_emit_tsd_multivalue.mjs'] + + self.get_cflags()) + actual = read_file('test_emit_tsd_multivalue.d.ts') + self.assertContained('_make_pair(_0: number, _1: number): [number, number];', actual) + @requires_dev_dependency('typescript') def test_emit_tsd_heap(self): self.run_process([EMCC, test_file('other/test_emit_tsd.c'), diff --git a/tools/emscripten.py b/tools/emscripten.py index c801a7465a6e0..bb63d76c08a1b 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -695,11 +695,15 @@ def create_tsd(metadata, embind_tsd): for index, type in enumerate(functype.params): arguments.append(f"_{index}: {type_to_ts_type(type)}") out += f' {mangled}({", ".join(arguments)}): ' - assert len(functype.returns) <= 1, 'One return type only supported' - if functype.returns: + if len(functype.returns) == 0: + ret_ts_type = 'void' + elif len(functype.returns) == 1: ret_ts_type = type_to_ts_type(functype.returns[0]) else: - ret_ts_type = 'void' + # Multi-value returns are valid wasm (e.g. wasm-bindgen emits them + # for `pub fn foo() -> String` as a (ptr, len) pair). Surface them + # as a TS tuple type; downstream glue (or .d.ts mergers) can refine. + ret_ts_type = '[' + ', '.join(type_to_ts_type(t) for t in functype.returns) + ']' if settings.ASYNCIFY == 2 and any(fnmatch.fnmatch(name, pat) for pat in settings.ASYNCIFY_EXPORTS): ret_ts_type = f'Promise<{ret_ts_type}>' out += f'{ret_ts_type};\n'