From a62b0054f5c9cef96303c5ed65a41e4692489d59 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Mon, 6 Oct 2025 21:52:51 -0700 Subject: [PATCH 1/2] [Swiftify] Cast OpaquePointer to UnsafeRawPointer before RawSpan The initializers for RawSpan and UnsafeRawBufferPointer take an UnsafeRawPointer, so when the underlying function returns an OpaquePointer we need to cast it first. rdar://162091516 --- .../SwiftMacros/SwiftifyImportMacro.swift | 33 ++++-- .../SwiftifyImport/SizedBy/OpaqueReturn.swift | 105 ++++++++++++++++++ 2 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift diff --git a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift index 32123e1de0aaf..2b20a16d0cf10 100644 --- a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift @@ -349,6 +349,16 @@ func transformType( return mainType } +func peelOptionalType(_ type: TypeSyntax) -> TypeSyntax { + if let optType = type.as(OptionalTypeSyntax.self) { + return optType.wrappedType + } + if let impOptType = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { + return impOptType.wrappedType + } + return type +} + func isMutablePointerType(_ type: TypeSyntax) -> Bool { if let optType = type.as(OptionalTypeSyntax.self) { return isMutablePointerType(optType.wrappedType) @@ -757,14 +767,14 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder { if unsafe _resultValue == nil { return nil } else { - return unsafe _swiftifyOverrideLifetime(\(raw: cast)(\(raw: startLabel): _resultValue!, \(raw: countLabel): Int(\(countExpr))), copying: ()) + return unsafe _swiftifyOverrideLifetime(\(raw: cast)(\(raw: startLabel): \(raw: castOpaquePointerToRawPointer("_resultValue!")), \(raw: countLabel): Int(\(countExpr))), copying: ()) } }() """ } else { expr = """ - \(raw: cast)(\(raw: startLabel): \(call), \(raw: countLabel): Int(\(countExpr))) + \(raw: cast)(\(raw: startLabel): \(castOpaquePointerToRawPointer(call)), \(raw: countLabel): Int(\(countExpr))) """ } if generateSpan { @@ -772,6 +782,15 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder { } return "unsafe \(expr)" } + + func castOpaquePointerToRawPointer(_ expr: ExprSyntax) -> ExprSyntax { + let type = peelOptionalType(oldType) + if type.canRepresentBasicType(type: OpaquePointer.self) { + return ExprSyntax("unsafe UnsafeRawPointer(\(expr))") + } + return expr + } + } struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBoundsThunkBuilder { @@ -896,16 +915,6 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds return "_\(raw: name)Count" } - func peelOptionalType(_ type: TypeSyntax) -> TypeSyntax { - if let optType = type.as(OptionalTypeSyntax.self) { - return optType.wrappedType - } - if let impOptType = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) { - return impOptType.wrappedType - } - return type - } - func castPointerToTargetType(_ baseAddress: ExprSyntax) throws -> ExprSyntax { let type = peelOptionalType(getParam(signature, index).type) if type.canRepresentBasicType(type: OpaquePointer.self) { diff --git a/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift b/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift new file mode 100644 index 0000000000000..3d056f8a0535f --- /dev/null +++ b/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift @@ -0,0 +1,105 @@ +// REQUIRES: swift_swift_parser + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend %t/test.swift -typecheck -plugin-path %swift-plugin-dir -strict-memory-safety -verify +// RUN: %target-swift-frontend %t/test.swift -typecheck -plugin-path %swift-plugin-dir -strict-memory-safety -dump-macro-expansions 2> %t/expansions.txt +// RUN: diff %t/expansions.txt %t/expansions.txt.expected + +//--- test.swift +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size")) +public func nonnullUnsafeRawBufferPointer(_ size: CInt) -> OpaquePointer {} + +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size")) +public func nullableUnsafeRawBufferPointer(_ size: CInt) -> OpaquePointer? {} + +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size")) +public func impNullableUnsafeRawBufferPointer(_ size: CInt) -> OpaquePointer! {} + +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size"), .sizedBy(pointer: .param(1), size: "size"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy)) +public func nonnullSpan(p: OpaquePointer, size: CInt) -> OpaquePointer {} + +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size"), .sizedBy(pointer: .param(1), size: "size"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy)) +public func nullableSpan(p: OpaquePointer?, _ size: CInt) -> OpaquePointer? {} + +@_SwiftifyImport(.sizedBy(pointer: .return, size: "size"), .sizedBy(pointer: .param(1), size: "size"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy)) +public func impNullableSpan(p: OpaquePointer!, _ size: CInt) -> OpaquePointer! {} + +//--- expansions.txt.expected +@__swiftmacro_4test29nonnullUnsafeRawBufferPointer15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload +public func nonnullUnsafeRawBufferPointer(_ size: CInt) -> UnsafeRawBufferPointer { + return unsafe UnsafeRawBufferPointer(start: unsafe UnsafeRawPointer(unsafe nonnullUnsafeRawBufferPointer(size)), count: Int(size)) +} +------------------------------ +@__swiftmacro_4test30nullableUnsafeRawBufferPointer15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload +public func nullableUnsafeRawBufferPointer(_ size: CInt) -> UnsafeRawBufferPointer? { + return unsafe { () in + let _resultValue = unsafe nullableUnsafeRawBufferPointer(size) + if unsafe _resultValue == nil { + return nil + } else { + return unsafe _swiftifyOverrideLifetime(UnsafeRawBufferPointer(start: unsafe UnsafeRawPointer(_resultValue!), count: Int(size)), copying: ()) + } + }() +} +------------------------------ +@__swiftmacro_4test33impNullableUnsafeRawBufferPointer15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_disfavoredOverload +public func impNullableUnsafeRawBufferPointer(_ size: CInt) -> UnsafeRawBufferPointer { + return unsafe UnsafeRawBufferPointer(start: unsafe UnsafeRawPointer(unsafe impNullableUnsafeRawBufferPointer(size)), count: Int(size)) +} +------------------------------ +@__swiftmacro_4test11nonnullSpan15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_lifetime(copy p) @_disfavoredOverload +public func nonnullSpan(p: RawSpan) -> RawSpan { + let size = CInt(exactly: p.byteCount)! + return unsafe _swiftifyOverrideLifetime(RawSpan(_unsafeStart: unsafe UnsafeRawPointer(unsafe p.withUnsafeBytes { _pPtr in + return unsafe nonnullSpan(p: OpaquePointer(_pPtr.baseAddress!), size: size) + }), byteCount: Int(size)), copying: ()) +} +------------------------------ +@__swiftmacro_4test12nullableSpan15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_lifetime(copy p) @_disfavoredOverload +public func nullableSpan(p: RawSpan?) -> RawSpan? { + let size = CInt(exactly: p?.byteCount ?? 0)! + return unsafe _swiftifyOverrideLifetime({ () in + let _resultValue = { () in + return if p == nil { + unsafe nullableSpan(p: nil, size) + } else { + unsafe p!.withUnsafeBytes { _pPtr in + return unsafe nullableSpan(p: OpaquePointer(_pPtr.baseAddress), size) + } + } + }() + if unsafe _resultValue == nil { + return nil + } else { + return unsafe _swiftifyOverrideLifetime(RawSpan(_unsafeStart: unsafe UnsafeRawPointer(_resultValue!), byteCount: Int(size)), copying: ()) + } + }(), copying: ()) +} +------------------------------ +@__swiftmacro_4test15impNullableSpan15_SwiftifyImportfMp_.swift +------------------------------ +/// This is an auto-generated wrapper for safer interop +@_alwaysEmitIntoClient @_lifetime(copy p) @_disfavoredOverload +public func impNullableSpan(p: RawSpan) -> RawSpan { + let size = CInt(exactly: p.byteCount)! + return unsafe _swiftifyOverrideLifetime(RawSpan(_unsafeStart: unsafe UnsafeRawPointer(unsafe p.withUnsafeBytes { _pPtr in + return unsafe impNullableSpan(p: OpaquePointer(_pPtr.baseAddress!), size) + }), byteCount: Int(size)), copying: ()) +} +------------------------------ From 7d5fd7d9f544c50ad1f5b86316cdc443cd6d3c9f Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Sat, 11 Oct 2025 16:33:24 -0700 Subject: [PATCH 2/2] filter out swift runtime warnings --- test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift b/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift index 3d056f8a0535f..91f88fdf0d72c 100644 --- a/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift +++ b/test/Macros/SwiftifyImport/SizedBy/OpaqueReturn.swift @@ -3,8 +3,9 @@ // RUN: %empty-directory(%t) // RUN: split-file %s %t // RUN: %target-swift-frontend %t/test.swift -typecheck -plugin-path %swift-plugin-dir -strict-memory-safety -verify -// RUN: %target-swift-frontend %t/test.swift -typecheck -plugin-path %swift-plugin-dir -strict-memory-safety -dump-macro-expansions 2> %t/expansions.txt -// RUN: diff %t/expansions.txt %t/expansions.txt.expected +// RUN: %target-swift-frontend %t/test.swift -typecheck -plugin-path %swift-plugin-dir -strict-memory-safety -dump-macro-expansions 2> %t/tmp.txt +// RUN: %FileCheck --dry-run --ignore-runtime-warnings > %t/expansions.txt < %t/tmp.txt +// RUN: diff --strip-trailing-cr %t/expansions.txt %t/expansions.txt.expected //--- test.swift @_SwiftifyImport(.sizedBy(pointer: .return, size: "size")) @@ -103,3 +104,4 @@ public func impNullableSpan(p: RawSpan) -> RawSpan { }), byteCount: Int(size)), copying: ()) } ------------------------------ +