Sometimes, when using legacy C APIs, you need many UnsafePointers to various values; especially when working with CoreFoundation style APIs. Currently, you might write something like this.
withUnsafePointer(to: foo) { foo in
withUnsafePointer(to: bar) { bar in
withUnsafePointer(to: baz) { baz in
fooBar(foo, bar, baz)
}
}
}
This amount of nesting and indentation is ugly and can be difficult to work with. A variadic macro could expand to the above code, but look like this.
#withUnsafePointer(to: foo, bar, baz) { foo, bar, baz in
fooBar(foo, bar, baz)
}
2 Likes
This is a sample implementation I made.
public struct WithUnsafePointerMacro: ExpressionMacro {
public enum Diagnostics: String, Error, DiagnosticMessage {
public var message: String {
switch self {
case .didNotProvideTrailingClosure:
return "#withUnsafePointer(to:body:) requires a trailing closure"
case .closureNeedsExplicitInput:
return "#withUnsafePointer(to:body:) requires an explicit closure parameter name for each closure parameter."
}
}
public var diagnosticID: SwiftDiagnostics.MessageID {
MessageID(domain: "WithUnsafePointerMacro", id: rawValue)
}
public var severity: DiagnosticSeverity { .error }
case didNotProvideTrailingClosure
case closureNeedsExplicitInput
func error(in context: some MacroExpansionContext, node: Syntax) -> Self {
context.diagnose(Diagnostic(node: node, message: self))
return self
}
}
public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
guard let closure = node.trailingClosure else {
throw Diagnostics.didNotProvideTrailingClosure.error(in: context, node: Syntax(node))
}
guard let input = closure.signature?.input?.as(ClosureParamListSyntax.self) else {
if node.argumentList.isEmpty {
return "\(closure)()"
} else {
throw Diagnostics.closureNeedsExplicitInput.error(in: context, node: Syntax(closure))
}
}
var parameterNames = Array(input.map { $0.name }.reversed())
var parameterValues = Array(node.argumentList.map { $0.expression }.reversed())
let firstName = parameterNames.removeFirst()
let firstValue = parameterValues.removeFirst()
var returnExpr: ExprSyntax = """
withUnsafePointer(to: \(firstValue)) { \(firstName.trimmed) in
\(closure.statements.trimmed)
}
"""
for (name, value) in zip(parameterNames, parameterValues) {
returnExpr = """
withUnsafePointer(to: \(value)) { \(name.trimmed) in
\(returnExpr)
}
"""
}
return returnExpr
}
}
1 Like
Alternatively, we could just wait until variadic generics are ironed out, and write this as a function instead.
func withUnsafePointer<each T, Output>(to pointee: repeat each T, body: (repeat UnsafePointer<each T>) throws -> Output) rethrows -> Output {
let pointer = (repeat UnsafeMutablePointer<(each T)>.allocate(capacity: 1))
defer { repeat ((each pointer).deallocate()) }
repeat ((each pointer).initialize(to: (each pointee)))
return try body(repeat each pointer)
}
However, currently this does not compile.
You could also make this better using a tuple and getting a pointer to that, but it's still not ideal.
withUnsafePointer(to: (foo, bar, baz)) { pointer in
let foo = pointer.pointer(to: \.0)
let bar = pointer.pointer(to: \.1)
let baz = pointer.pointer(to: \.2)
return fooBar(foo, bar, baz)
}
5 Likes
tera
4
As a practical workaround maybe just these overloads until a better solution is available:
typealias UP = UnsafePointer
func withUnsafePointer<R, T1, T2>(to v1: T1, _ v2: T2, _ body: (UP<T1>, UP<T2>) throws -> R) rethrows -> R {
try withUnsafePointer(to: v1) { p1 in
try withUnsafePointer(to: v2) { p2 in
try body(p1, p2)
}
}
}
func withUnsafePointer<R, T1, T2, T3>(to v1: T1, _ v2: T2, _ v3: T3, _ body: (UP<T1>, UP<T2>, UP<T3>) throws -> R) rethrows -> R {
.....
}
// etc
Like how @ViewBuilder has 10 overloads?
3 Likes
JuneBash
(June Bash)
6
I believe that lack of compilation might be a bug? This seems to type-check correctly, but crashes the compiler.
Crashing code
func withUnsafePointers<each T, R>(
to values: repeat each T,
operation: (repeat (UnsafePointer<each T>)) throws -> R
) rethrows -> R {
func makePointer<Value>(to value: Value) -> UnsafePointer<Value> {
let pointer = UnsafeMutablePointer<Value>.allocate(capacity: 1)
pointer.initialize(to: value)
return UnsafePointer(pointer)
}
let pointers = repeat makePointer(to: each values)
defer { repeat (each pointers).deallocate() }
return try operation(repeat each pointers)
}
let x = withUnsafePointers(to: 4, "hello", 9.8 as Float) { intptr, strptr, fltptr in
makeThingWithPointers(int: intptr, str: strptr, flt: fltptr)
}
func makeThingWithPointers(
int: UnsafePointer<Int>,
str: UnsafePointer<String>,
flt: UnsafePointer<Float>
) -> String {
""
}
Crash log
SwiftCompile normal arm64 Compiling\ VariadicPointerTest.swift /Users/jbash/repos/Playgrounds/VariadicPointerTest/Sources/VariadicPointerTest/VariadicPointerTest.swift (in target 'VariadicPointerTest' from project 'VariadicPointerTest')
cd /Users/jbash/repos/Playgrounds/VariadicPointerTest
builtin-swiftTaskExecution -- /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/jbash/repos/Playgrounds/VariadicPointerTest/Sources/VariadicPointerTest/VariadicPointerTest.swift -emit-dependencies-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.d -emit-const-values-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.swiftconstvalues -emit-reference-dependencies-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.swiftdeps -serialize-diagnostics-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.dia -target arm64-apple-ios12.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk -I /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos -I /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib -F /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos -F /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks -F /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/Developer/Library/Frameworks -no-color-diagnostics -enable-testing -g -module-cache-path /Users/jbash/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -swift-version 5 -enforce-exclusivity\=checked -Onone -D SWIFT_PACKAGE -D DEBUG -D Xcode -serialize-debugging-options -const-gather-protocols-file /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest_const_extract_protocols.json -empty-abi-descriptor -plugin-path /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -validate-clang-modules-once -clang-build-session-file /Users/jbash/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/jbash/repos/Playgrounds/VariadicPointerTest -resource-dir /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -ivfsstatcache -Xcc /Users/jbash/Library/Developer/Xcode/DerivedData/SDKStatCaches.noindex/iphoneos17.0-21A5248u-b23d71c8e10368373ae3270184a0e6b4.sdkstatcache -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/swift-overrides.hmap -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos/include -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources-normal/arm64 -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources/arm64 -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources -Xcc -DSWIFT_PACKAGE -Xcc -DDEBUG\=1 -module-name VariadicPointerTest -frontend-parseable-output -disable-clang-spi -target-sdk-version 17.0 -target-sdk-name iphoneos17.0 -parse-as-library -o /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.o -index-unit-output-path /VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.o -index-store-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Index.noindex/DataStore -index-system-modules
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0. Program arguments: /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/jbash/repos/Playgrounds/VariadicPointerTest/Sources/VariadicPointerTest/VariadicPointerTest.swift -emit-dependencies-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.d -emit-const-values-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.swiftconstvalues -emit-reference-dependencies-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.swiftdeps -serialize-diagnostics-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.dia -target arm64-apple-ios12.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk -I /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos -I /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib -F /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos -F /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks -F /Applications/Xcode15-beta1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/Developer/Library/Frameworks -no-color-diagnostics -enable-testing -g -module-cache-path /Users/jbash/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -swift-version 5 -enforce-exclusivity=checked -Onone -D SWIFT_PACKAGE -D DEBUG -D Xcode -serialize-debugging-options -const-gather-protocols-file /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest_const_extract_protocols.json -empty-abi-descriptor -plugin-path /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -validate-clang-modules-once -clang-build-session-file /Users/jbash/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/jbash/repos/Playgrounds/VariadicPointerTest -resource-dir /Applications/Xcode15-beta1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -ivfsstatcache -Xcc /Users/jbash/Library/Developer/Xcode/DerivedData/SDKStatCaches.noindex/iphoneos17.0-21A5248u-b23d71c8e10368373ae3270184a0e6b4.sdkstatcache -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/swift-overrides.hmap -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Products/Debug-iphoneos/include -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources-normal/arm64 -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources/arm64 -Xcc -I/Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/DerivedSources -Xcc -DSWIFT_PACKAGE -Xcc -DDEBUG=1 -module-name VariadicPointerTest -frontend-parseable-output -disable-clang-spi -target-sdk-version 17.0 -target-sdk-name iphoneos17.0 -parse-as-library -o /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Build/Intermediates.noindex/VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.o -index-unit-output-path /VariadicPointerTest.build/Debug-iphoneos/VariadicPointerTest.build/Objects-normal/arm64/VariadicPointerTest.o -index-store-path /Users/jbash/Library/Developer/Xcode/DerivedData/VariadicPointerTest-fpcjylqvuqqnqwfzfahkauuzfuhj/Index.noindex/DataStore -index-system-modules
1. Apple Swift version 5.9 (swiftlang-5.9.0.114.6 clang-1500.0.27.1)
2. Compiling with the current language version
3. While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "/Users/jbash/repos/Playgrounds/VariadicPointerTest/Sources/VariadicPointerTest/VariadicPointerTest.swift")
4. While silgen emitFunction SIL function "@$s19VariadicPointerTest18withUnsafePointers2to9operationq_xxQp_q_SPyxGxQpKXEtKRvzr0_lF".
for 'withUnsafePointers(to:operation:)' (at /Users/jbash/repos/Playgrounds/VariadicPointerTest/Sources/VariadicPointerTest/VariadicPointerTest.swift:1:8)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0 swift-frontend 0x0000000105e6391c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1 swift-frontend 0x0000000105e62ae0 llvm::sys::RunSignalHandlers() + 112
2 swift-frontend 0x0000000105e63f1c SignalHandler(int) + 352
3 libsystem_platform.dylib 0x00000001a7832a84 _sigtramp + 56
4 swift-frontend 0x0000000100fdd854 void llvm::function_ref<void (swift::SILValue, swift::SILValue, swift::SILValue)>::callback_fn<(anonymous namespace)::RValueEmitter::visitPackExpansionExpr(swift::PackExpansionExpr*, swift::Lowering::SGFContext)::$_7>(long, swift::SILValue, swift::SILValue, swift::SILValue) + 96
5 swift-frontend 0x000000010100f2e0 swift::Lowering::SILGenFunction::emitDynamicPackLoop(swift::SILLocation, swift::CanTypeWrapper<swift::PackType>, unsigned int, swift::SILValue, swift::SILValue, swift::GenericEnvironment*, bool, llvm::function_ref<void (swift::SILValue, swift::SILValue, swift::SILValue)>) + 1340
6 swift-frontend 0x0000000100fce2e4 (anonymous namespace)::RValueEmitter::visitPackExpansionExpr(swift::PackExpansionExpr*, swift::Lowering::SGFContext) + 332
7 swift-frontend 0x0000000100fb8f50 swift::Lowering::SILGenFunction::emitExprInto(swift::Expr*, swift::Lowering::Initialization*, llvm::Optional<swift::SILLocation>) + 128
8 swift-frontend 0x0000000100fa3cd8 swift::Lowering::SILGenFunction::emitPatternBinding(swift::PatternBindingDecl*, unsigned int, bool) + 1984
9 swift-frontend 0x0000000100f47114 swift::ASTVisitor<swift::Lowering::SILGenFunction, void, void, void, void, void, void>::visit(swift::Decl*) + 140
10 swift-frontend 0x0000000101044408 swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 4704
11 swift-frontend 0x0000000100fe8c9c swift::Lowering::SILGenFunction::emitFunction(swift::FuncDecl*) + 632
12 swift-frontend 0x0000000100f436d0 swift::Lowering::SILGenModule::emitFunctionDefinition(swift::SILDeclRef, swift::SILFunction*) + 7640
13 swift-frontend 0x0000000100f4502c emitOrDelayFunction(swift::Lowering::SILGenModule&, swift::SILDeclRef) + 168
14 swift-frontend 0x0000000100f418e4 swift::Lowering::SILGenModule::emitFunction(swift::FuncDecl*) + 292
15 swift-frontend 0x0000000100f47cec swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const + 2940
16 swift-frontend 0x0000000101042de4 swift::SimpleRequest<swift::ASTLoweringRequest, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>> (swift::ASTLoweringDescriptor), (swift::RequestFlags)9>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) + 200
17 swift-frontend 0x0000000100f4b8ec llvm::Expected<swift::ASTLoweringRequest::OutputType> swift::Evaluator::getResultUncached<swift::ASTLoweringRequest>(swift::ASTLoweringRequest const&) + 620
18 swift-frontend 0x0000000100894d04 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1680
19 swift-frontend 0x00000001008931e0 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4216
20 swift-frontend 0x0000000100858a58 swift::mainEntry(int, char const**) + 4112
21 dyld 0x00000001a74abf28 start + 2236
Command SwiftCompile failed with a nonzero exit code
Yeah, I think the compiler functionality for variadic generics aren't fully implemented yet.