Hi all,
I’m running into a crash when trying to wrap os_log in a helper function that takes an @autoclosure message and arguments. Here’s a simplified version:
import Foundation
import os.log
let testLog: OSLog = OSLog(subsystem: "test", category: "Test")
func runtimeIssues(
_ message: @autoclosure () -> StaticString,
_ args: @autoclosure () -> [CVarArg] = []
) {
// ❌ This will crash:
// os_log(.fault, log: testLog, message(), args())
// ✅ But if I unsafeBitCast os_log to a version that accepts [CVarArg],
// it works without crashing:
unsafeBitCast(
os_log as (OSLogType, UnsafeRawPointer, OSLog, StaticString, CVarArg...) -> Void,
to: ((OSLogType, UnsafeRawPointer, OSLog, StaticString, [CVarArg]) -> Void).self
)(.fault, #dsohandle, testLog, message(), args())
}
runtimeIssues("Error %s", ["test"])
When I call the function directly with os_log(.fault, log: testLog, message(), args()), I get a runtime crash:
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[Swift.__SwiftDeferredNSArray UTF8String]:
unrecognized selector sent to instance 0x600000de2780'
But if I bitcast os_log to a version that takes [CVarArg], everything works fine.
Context
I’d like args to be @autoclosure, which means I can’t use CVarArg... directly in the parameter list (since variadics can only appear at the call site, not as return values). That’s why I ended up with @autoclosure () -> [CVarArg].
So my questions are:
- What’s the fundamental difference between CVarArg... and [CVarArg] here that causes the crash?
- Is the unsafeBitCast workaround the “right” way to make this work, or is there a safer/official way?
- Is there a Swift-native approach to forwarding variadic CVarArg... without this crash?
Any insight into why this happens would be appreciated.