Skip to content

[Swiftify] Fix transformation sort, and dropped bounds checks #80317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ struct CxxSpanThunkBuilder: ParamPointerBoundsThunkBuilder {
let isSizedBy: Bool = false

func buildBoundsChecks() throws -> [CodeBlockItemSyntax.Item] {
return []
return try base.buildBoundsChecks()
}

func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
Expand Down Expand Up @@ -447,7 +447,7 @@ struct CxxSpanReturnThunkBuilder: BoundsCheckedThunkBuilder {
public let node: SyntaxProtocol

func buildBoundsChecks() throws -> [CodeBlockItemSyntax.Item] {
return []
return try base.buildBoundsChecks()
}

func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
Expand Down Expand Up @@ -1142,7 +1142,13 @@ public struct SwiftifyImportMacro: PeerMacro {
parsedArgs.sort { a, b in
// make sure return value cast to Span happens last so that withUnsafeBufferPointer
// doesn't return a ~Escapable type
(a.pointerIndex != .return && b.pointerIndex == .return) || paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
if a.pointerIndex != .return && b.pointerIndex == .return {
return true
}
if a.pointerIndex == .return && b.pointerIndex != .return {
return false
}
return paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
}
let baseBuilder = FunctionCallBuilder(funcDecl)

Expand Down
54 changes: 54 additions & 0 deletions test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ struct X {
func myFunc5() -> SpanOfInt {}
}

@_SwiftifyImport(.lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy),
.sizedBy(pointer: .param(2), size: "count * size"),
.nonescaping(pointer: .param(2)),
typeMappings: ["SpanOfInt" : "std.span<CInt>"])
func myFunc6(_ span: SpanOfInt, _ ptr: UnsafeRawPointer, _ count: CInt, _ size: CInt) -> SpanOfInt {
}

@_SwiftifyImport(.sizedBy(pointer: .param(2), size: "count * size"),
.lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy),
.nonescaping(pointer: .param(2)),
typeMappings: ["SpanOfInt" : "std.span<CInt>"])
func myFunc7(_ span: SpanOfInt, _ ptr: UnsafeRawPointer, _ count: CInt, _ size: CInt) -> SpanOfInt {
}

@_SwiftifyImport(.sizedBy(pointer: .param(1), size: "count * size"),
.nonescaping(pointer: .param(1)),
.lifetimeDependence(dependsOn: .param(2), pointer: .return, type: .copy),
typeMappings: ["SpanOfInt" : "std.span<CInt>"])
func myFunc8(_ ptr: UnsafeRawPointer, _ span: SpanOfInt, _ count: CInt, _ size: CInt) -> SpanOfInt {
}

// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
// CHECK-NEXT: func myFunc(_ span: Span<CInt>) -> Span<CInt> {
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc(SpanOfInt(span))), copying: ())
Expand All @@ -56,3 +77,36 @@ struct X {
// CHECK-NEXT: func myFunc5() -> Span<CInt> {
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc5()), copying: ())
// CHECK-NEXT: }

// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
// CHECK-NEXT: func myFunc6(_ span: Span<CInt>, _ ptr: RawSpan, _ count: CInt, _ size: CInt) -> Span<CInt> {
// CHECK-NEXT: let _ptrCount: some BinaryInteger = count * size
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
// CHECK-NEXT: }
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
// CHECK-NEXT: return unsafe myFunc6(SpanOfInt(span), _ptrPtr.baseAddress!, count, size)
// CHECK-NEXT: }), copying: ())
// CHECK-NEXT: }

// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
// CHECK-NEXT: func myFunc7(_ span: Span<CInt>, _ ptr: RawSpan, _ count: CInt, _ size: CInt) -> Span<CInt> {
// CHECK-NEXT: let _ptrCount: some BinaryInteger = count * size
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
// CHECK-NEXT: }
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
// CHECK-NEXT: return unsafe myFunc7(SpanOfInt(span), _ptrPtr.baseAddress!, count, size)
// CHECK-NEXT: }), copying: ())
// CHECK-NEXT: }

// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
// CHECK-NEXT: func myFunc8(_ ptr: RawSpan, _ span: Span<CInt>, _ count: CInt, _ size: CInt) -> Span<CInt> {
// CHECK-NEXT: let _ptrCount: some BinaryInteger = count * size
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
// CHECK-NEXT: }
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
// CHECK-NEXT: return unsafe myFunc8(_ptrPtr.baseAddress!, SpanOfInt(span), count, size)
// CHECK-NEXT: }), copying: ())
// CHECK-NEXT: }