Skip to content

Commit 160bb41

Browse files
committed
[Swiftify] Fix return value transformation being applied last
Casting the return value to Span must be done outside withUnsafeBufferPointer, to prevent returning a ~Escapable type from a function without lifetime info. To do this we sort the transformations so that the return value transformation is performed last. There was a bug in the comparison, so the sorting was not always done correctly. rdar://147934170
1 parent c7af94b commit 160bb41

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,13 @@ public struct SwiftifyImportMacro: PeerMacro {
11421142
parsedArgs.sort { a, b in
11431143
// make sure return value cast to Span happens last so that withUnsafeBufferPointer
11441144
// doesn't return a ~Escapable type
1145-
(a.pointerIndex != .return && b.pointerIndex == .return) || paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
1145+
if a.pointerIndex != .return && b.pointerIndex == .return {
1146+
return true
1147+
}
1148+
if a.pointerIndex == .return && b.pointerIndex != .return {
1149+
return false
1150+
}
1151+
return paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
11461152
}
11471153
let baseBuilder = FunctionCallBuilder(funcDecl)
11481154

test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11

22
// REQUIRES: swift_swift_parser
33

4-
// FIXME: buggy sort order for return value transformation gives compilation error in expansion
5-
// RUN: not %target-swift-frontend %s -enable-experimental-cxx-interop -I %S/Inputs -Xcc -std=c++20 -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions -verify -strict-memory-safety 2>&1 | %FileCheck --match-full-lines %s
4+
// RUN: %target-swift-frontend %s -enable-experimental-cxx-interop -I %S/Inputs -Xcc -std=c++20 -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions -verify -strict-memory-safety 2>&1 | %FileCheck --match-full-lines %s
65

76
// FIXME swift-ci linux tests do not support std::span
87
// UNSUPPORTED: OS=linux-gnu
@@ -40,6 +39,20 @@ struct X {
4039
func myFunc6(_ span: SpanOfInt, _ ptr: UnsafeRawPointer, _ count: CInt, _ size: CInt) -> SpanOfInt {
4140
}
4241

42+
@_SwiftifyImport(.sizedBy(pointer: .param(2), size: "count * size"),
43+
.lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy),
44+
.nonescaping(pointer: .param(2)),
45+
typeMappings: ["SpanOfInt" : "std.span<CInt>"])
46+
func myFunc7(_ span: SpanOfInt, _ ptr: UnsafeRawPointer, _ count: CInt, _ size: CInt) -> SpanOfInt {
47+
}
48+
49+
@_SwiftifyImport(.sizedBy(pointer: .param(1), size: "count * size"),
50+
.nonescaping(pointer: .param(1)),
51+
.lifetimeDependence(dependsOn: .param(2), pointer: .return, type: .copy),
52+
typeMappings: ["SpanOfInt" : "std.span<CInt>"])
53+
func myFunc8(_ ptr: UnsafeRawPointer, _ span: SpanOfInt, _ count: CInt, _ size: CInt) -> SpanOfInt {
54+
}
55+
4356
// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
4457
// CHECK-NEXT: func myFunc(_ span: Span<CInt>) -> Span<CInt> {
4558
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc(SpanOfInt(span))), copying: ())
@@ -71,7 +84,29 @@ func myFunc6(_ span: SpanOfInt, _ ptr: UnsafeRawPointer, _ count: CInt, _ size:
7184
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
7285
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
7386
// CHECK-NEXT: }
74-
// CHECK-NEXT: return unsafe ptr.withUnsafeBytes { _ptrPtr in
75-
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc6(SpanOfInt(span), _ptrPtr.baseAddress!, count, size)), copying: ())
87+
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
88+
// CHECK-NEXT: return unsafe myFunc6(SpanOfInt(span), _ptrPtr.baseAddress!, count, size)
89+
// CHECK-NEXT: }), copying: ())
90+
// CHECK-NEXT: }
91+
92+
// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
93+
// CHECK-NEXT: func myFunc7(_ span: Span<CInt>, _ ptr: RawSpan, _ count: CInt, _ size: CInt) -> Span<CInt> {
94+
// CHECK-NEXT: let _ptrCount: some BinaryInteger = count * size
95+
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
96+
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
97+
// CHECK-NEXT: }
98+
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
99+
// CHECK-NEXT: return unsafe myFunc7(SpanOfInt(span), _ptrPtr.baseAddress!, count, size)
100+
// CHECK-NEXT: }), copying: ())
101+
// CHECK-NEXT: }
102+
103+
// CHECK: @_alwaysEmitIntoClient @lifetime(copy span)
104+
// CHECK-NEXT: func myFunc8(_ ptr: RawSpan, _ span: Span<CInt>, _ count: CInt, _ size: CInt) -> Span<CInt> {
105+
// CHECK-NEXT: let _ptrCount: some BinaryInteger = count * size
106+
// CHECK-NEXT: if ptr.byteCount < _ptrCount || _ptrCount < 0 {
107+
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
76108
// CHECK-NEXT: }
109+
// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: ptr.withUnsafeBytes { _ptrPtr in
110+
// CHECK-NEXT: return unsafe myFunc8(_ptrPtr.baseAddress!, SpanOfInt(span), count, size)
111+
// CHECK-NEXT: }), copying: ())
77112
// CHECK-NEXT: }

0 commit comments

Comments
 (0)