From e82c2497d95df1b77f161bddd85e5fc15108f750 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Sep 2025 22:36:33 +0900 Subject: [PATCH] [wasm] Add support for __builtin_va_list type mapping (#84029) Cover all va_list family of types just aliased by __builtin_va_list, including __isoc_va_list from wasi-libc. This enables proper C interop for variable argument functions on WASI targets. Close https://github.com/swiftlang/swift/issues/72398 # Conflicts: # test/ClangImporter/Inputs/custom-modules/module.modulemap --- lib/ClangImporter/MappedTypes.def | 3 +++ .../Inputs/custom-modules/CVarArgs.h | 7 +++++++ .../Inputs/custom-modules/module.modulemap | 5 +++++ test/ClangImporter/ctypes_valist_wasm.swift | 18 ++++++++++++++++++ test/stdlib/VarArgs.swift | 19 +++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 test/ClangImporter/Inputs/custom-modules/CVarArgs.h create mode 100644 test/ClangImporter/ctypes_valist_wasm.swift diff --git a/lib/ClangImporter/MappedTypes.def b/lib/ClangImporter/MappedTypes.def index ea4610eb0e215..ca7b63a701250 100644 --- a/lib/ClangImporter/MappedTypes.def +++ b/lib/ClangImporter/MappedTypes.def @@ -129,6 +129,9 @@ MAP_STDLIB_TYPE("u_int64_t", UnsignedInt, 64, "UInt64", false, DoNothing) MAP_STDLIB_TYPE("va_list", VaList, 0, "CVaListPointer", false, DoNothing) MAP_STDLIB_TYPE("__gnuc_va_list", VaList, 0, "CVaListPointer", false, DoNothing) MAP_STDLIB_TYPE("__va_list", VaList, 0, "CVaListPointer", false, DoNothing) +// Cover all va_list family of types just aliased by __builtin_va_list, including +// __isoc_va_list from wasi-libc. +MAP_STDLIB_TYPE("__builtin_va_list", VaList, 0, "CVaListPointer", false, DoNothing) // libkern/OSTypes.h types. MAP_STDLIB_TYPE("UInt", UnsignedInt, 32, "CUnsignedInt", false, DoNothing) diff --git a/test/ClangImporter/Inputs/custom-modules/CVarArgs.h b/test/ClangImporter/Inputs/custom-modules/CVarArgs.h new file mode 100644 index 0000000000000..53fd7057a11d0 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/CVarArgs.h @@ -0,0 +1,7 @@ +#include +extern void takeVaList(va_list args); + +extern void takeVaList2(__builtin_va_list args); + +typedef __builtin_va_list my_builtin_va_list_alias; +extern void takeVaList3(my_builtin_va_list_alias args); diff --git a/test/ClangImporter/Inputs/custom-modules/module.modulemap b/test/ClangImporter/Inputs/custom-modules/module.modulemap index a827a3474db82..25f5d5b185590 100644 --- a/test/ClangImporter/Inputs/custom-modules/module.modulemap +++ b/test/ClangImporter/Inputs/custom-modules/module.modulemap @@ -275,3 +275,8 @@ module CommonName { module "Weird C Module" { header "WeirdCModule.h" } + +module CVarArgs { + header "CVarArgs.h" + export * +} diff --git a/test/ClangImporter/ctypes_valist_wasm.swift b/test/ClangImporter/ctypes_valist_wasm.swift new file mode 100644 index 0000000000000..5ec2856f85337 --- /dev/null +++ b/test/ClangImporter/ctypes_valist_wasm.swift @@ -0,0 +1,18 @@ +// RUN: %swift-frontend -target wasm32-unknown-wasip1 -parse-stdlib -module-name Swift -I %S/Inputs/custom-modules -typecheck %s + +// REQUIRES: CODEGENERATOR=WebAssembly + +import CVarArgs + +/// ===== Minimal stdlib definitions ===== +typealias Void = () +struct CVaListPointer {} +// Optional definition is required for isOptional check in ClangImporter +enum Optional {} +/// ====================================== + +func testVaList() { + let _: (CVaListPointer) -> Void = CVarArgs.takeVaList + let _: (CVaListPointer) -> Void = CVarArgs.takeVaList2 + let _: (CVaListPointer) -> Void = CVarArgs.takeVaList3 +} diff --git a/test/stdlib/VarArgs.swift b/test/stdlib/VarArgs.swift index 68437b90b6181..2f429faa88fbb 100644 --- a/test/stdlib/VarArgs.swift +++ b/test/stdlib/VarArgs.swift @@ -193,6 +193,25 @@ func test_varArgs7() { } test_varArgs7() +func test_varArgs8() { + // Check with vsnprintf as well just in case to make sure it works with C APIs + // imported from actual libc headers. Why checks with vprintf above are not enough? + // ClangImporter respects the vprintf interface re-defined in LibcShims.h but it could + // be slightly different than the one in platform libc on some platforms, including WASI. + // (e.g. wasi-libc defines `int vprintf(const char *__restrict, __isoc_va_list);` where + // `__isoc_va_list` is `typedef __builtin_va_list __isoc_va_list;`, but LibcShims.h aliases + // __builtin_va_list as `va_list`.) + // https://github.com/swiftlang/swift/issues/72398 + let string = withUnsafeTemporaryAllocation(of: CChar.self, capacity: 512) { buffer in + withVaList([CInt(123)]) { args in + _ = vsnprintf(buffer.baseAddress!, buffer.count, "%d", args) + } + return String(cString: buffer.baseAddress!) + } + print(string) + // CHECK: 123 +} +test_varArgs8() // CHECK: done. my_printf("done.")