mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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
218 lines
6.8 KiB
Swift
218 lines
6.8 KiB
Swift
// RUN: %target-run-stdlib-swift -parse-stdlib %s | %FileCheck %s
|
|
// REQUIRES: executable_test
|
|
|
|
import Swift
|
|
|
|
// FIXME: https://github.com/apple/swift/issues/57444
|
|
// Work around the inability for static-library based Swift runtime builds to
|
|
// directly link against Darwin.swiftmodule by using a benign dependency on
|
|
// StdlibUnittest.
|
|
import StdlibUnittest
|
|
runAllTests()
|
|
|
|
#if canImport(Darwin)
|
|
import Darwin
|
|
#if _runtime(_ObjC)
|
|
import CoreGraphics
|
|
#else
|
|
#if arch(x86_64) || arch(arm64)
|
|
typealias CGFloat = Double
|
|
#else
|
|
typealias CGFloat = Float
|
|
#endif
|
|
#endif
|
|
#elseif canImport(Glibc)
|
|
import Glibc
|
|
typealias CGFloat = Double
|
|
#elseif os(WASI)
|
|
import WASILibc
|
|
typealias CGFloat = Double
|
|
#elseif canImport(Android)
|
|
import Android
|
|
typealias CGFloat = Double
|
|
#elseif os(Windows)
|
|
import CRT
|
|
#if arch(x86_64) || arch(arm64)
|
|
typealias CGFloat = Double
|
|
#else
|
|
typealias CGFloat = Float
|
|
#endif
|
|
#else
|
|
#error("Unsupported platform")
|
|
#endif
|
|
|
|
func my_printf(_ format: String, _ arguments: CVarArg...) {
|
|
_ = withVaList(arguments) {
|
|
vprintf(format, $0)
|
|
}
|
|
}
|
|
|
|
func test_varArgs0() {
|
|
// CHECK: The answer to life and everything is 42, 42, -42, 3.14
|
|
my_printf(
|
|
"The answer to life and everything is %ld, %u, %d, %f\n",
|
|
42, UInt32(42), Int16(-42), 3.14159279)
|
|
}
|
|
test_varArgs0()
|
|
|
|
func test_varArgs1() {
|
|
var args = [CVarArg]()
|
|
|
|
var format = "dig it: "
|
|
for i in 0..<12 {
|
|
args.append(Int16(-i))
|
|
args.append(Float(i))
|
|
format += "%d %2g "
|
|
}
|
|
|
|
// CHECK: dig it: 0 0 -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -7 7 -8 8 -9 9 -10 10 -11 11
|
|
_ = withVaList(args) {
|
|
vprintf(format + "\n", $0)
|
|
}
|
|
}
|
|
test_varArgs1()
|
|
|
|
func test_varArgs3() {
|
|
var args = [CVarArg]()
|
|
|
|
let format = "pointers: '%p' '%p' '%p' '%p' '%p'\n"
|
|
args.append(OpaquePointer(bitPattern: 0x1234_5670)!)
|
|
args.append(OpaquePointer(bitPattern: 0x1234_5671)!)
|
|
args.append(UnsafePointer<Int>(bitPattern: 0x1234_5672)!)
|
|
args.append(UnsafeMutablePointer<Float>(bitPattern: 0x1234_5673)!)
|
|
|
|
#if _runtime(_ObjC)
|
|
args.append(AutoreleasingUnsafeMutablePointer<AnyObject>(
|
|
UnsafeMutablePointer<AnyObject>(bitPattern: 0x1234_5674)!))
|
|
#else
|
|
//Linux does not support AutoreleasingUnsafeMutablePointer; put placeholder.
|
|
args.append(UnsafeMutablePointer<Float>(bitPattern: 0x1234_5674)!)
|
|
#endif
|
|
|
|
// CHECK: {{pointers: '(0x)?0*12345670' '(0x)?0*12345671' '(0x)?0*12345672' '(0x)?0*12345673' '(0x)?0*12345674'}}
|
|
_ = withVaList(args) {
|
|
vprintf(format, $0)
|
|
}
|
|
}
|
|
test_varArgs3()
|
|
|
|
func test_varArgs4() {
|
|
// Verify alignment of va_list contents.
|
|
// On some architectures some types are better-
|
|
// aligned than Int and must be packaged with care.
|
|
|
|
let i8 = Int8(1)
|
|
let i16 = Int16(2)
|
|
let i32 = Int32(3)
|
|
let i64 = 4444444444444444 as Int64
|
|
|
|
let u8 = UInt8(10)
|
|
let u16 = UInt16(20)
|
|
let u32 = UInt32(30)
|
|
let u64 = 4040404040404040 as UInt64
|
|
|
|
let f32 = Float(1.1)
|
|
let f64 = Double(2.2)
|
|
let fCG = CGFloat(3.3)
|
|
|
|
my_printf("a %g %d %g %d %g %d a\n", f32, i8, f64, i8, fCG, i8)
|
|
my_printf("b %d %g %d %g %d %g %d b\n", i8, f32, i8, f64, i8, fCG, i8)
|
|
my_printf("c %d %d %d %d %d %lld %d c\n", i8, i16, i8, i32, i8, i64, i8)
|
|
my_printf("d %d %d %d %d %d %d %lld %d d\n",i8, i8, i16, i8, i32, i8, i64, i8)
|
|
my_printf("e %u %u %u %u %u %llu %u e\n", u8, u16, u8, u32, u8, u64, u8)
|
|
my_printf("f %u %u %u %u %u %u %llu %u f\n",u8, u8, u16, u8, u32, u8, u64, u8)
|
|
// CHECK: a 1.1 1 2.2 1 3.3 1 a
|
|
// CHECK: b 1 1.1 1 2.2 1 3.3 1 b
|
|
// CHECK: c 1 2 1 3 1 4444444444444444 1 c
|
|
// CHECK: d 1 1 2 1 3 1 4444444444444444 1 d
|
|
// CHECK: e 10 20 10 30 10 4040404040404040 10 e
|
|
// CHECK: f 10 10 20 10 30 10 4040404040404040 10 f
|
|
}
|
|
test_varArgs4()
|
|
|
|
func test_varArgs5() {
|
|
var args = [CVarArg]()
|
|
|
|
// Confirm the absence of a bug (on x86-64) wherein floats were stored in
|
|
// the GP register-save area after the SSE register-save area was
|
|
// exhausted, rather than spilling into the overflow argument area.
|
|
//
|
|
// This is not caught by test_varArgs1 above, because it exhausts the
|
|
// GP register-save area before the SSE area.
|
|
|
|
var format = "rdar-32547102: "
|
|
for i in 0..<12 {
|
|
args.append(Float(i))
|
|
format += "%.1f "
|
|
}
|
|
|
|
// CHECK: rdar-32547102: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0
|
|
_ = withVaList(args) {
|
|
vprintf(format + "\n", $0)
|
|
}
|
|
}
|
|
test_varArgs5()
|
|
|
|
func test_varArgs6() {
|
|
// Verify alignment of va_list contents when `Float80` is present.
|
|
let i8 = Int8(1)
|
|
let f32 = Float(1.1)
|
|
let f64 = Double(2.2)
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
let f80 = Float80(4.5)
|
|
my_printf("a %g %d %g %d %Lg %d %g a\n", f32, i8, f64, i8, f80, i8, f32)
|
|
my_printf("b %d %g %d %g %d %Lg %d %g b\n", i8, f32, i8, f64, i8, f80, i8, f32)
|
|
#else // just a dummy to make FileCheck happy, since it ignores `#if`s
|
|
let dummy = Double(4.5)
|
|
my_printf("a %g %d %g %d %g %d %g a\n", f32, i8, f64, i8, dummy, i8, f32)
|
|
my_printf("b %d %g %d %g %d %g %d %g b\n", i8, f32, i8, f64, i8, dummy, i8, f32)
|
|
#endif
|
|
// CHECK: a 1.1 1 2.2 1 4.5 1 1.1 a
|
|
// CHECK: b 1 1.1 1 2.2 1 4.5 1 1.1 b
|
|
}
|
|
test_varArgs6()
|
|
|
|
func test_varArgs7() {
|
|
#if canImport(Darwin) && arch(arm64)
|
|
let canTest = if #available(SwiftStdlib 6.2, *) { true } else { false }
|
|
#else
|
|
// va_list is more complicated on other targets so that behavior is not the
|
|
// same, skip the test by doing a fake print of the expected output. Also
|
|
// skip the test if we're testing against an older runtime without the fix.
|
|
let canTest = false
|
|
#endif
|
|
|
|
if canTest {
|
|
// Test a workaround for format specifiers and no arguments. We supply eight
|
|
// words of zeroed memory to give this predictable behavior.
|
|
my_printf("No parameters: %ld %ld %ld %ld %ld %ld %ld %ld\n")
|
|
} else {
|
|
my_printf("No parameters: 0 0 0 0 0 0 0 0\n")
|
|
}
|
|
// CHECK: No parameters: 0 0 0 0 0 0 0 0
|
|
}
|
|
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.")
|