Skip to content

Commit e08f701

Browse files
committed
[Swiftify] Emit Span for counted_by return values with lifetime info
__counted_by return values with .lifetimeDependence are now mapped to Span instead of UnsafeBufferPointer. Also fixes bug where std::span return values would map to Span even if lifetime dependence info was missing.
1 parent d43ff2d commit e08f701

File tree

4 files changed

+52
-8
lines changed

4 files changed

+52
-8
lines changed

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ struct CxxSpan: ParamInfo {
8888
return CxxSpanThunkBuilder(base: base, index: i - 1, signature: funcDecl.signature,
8989
typeMappings: typeMappings, node: original, nonescaping: nonescaping)
9090
case .return:
91+
if dependencies.isEmpty {
92+
return base
93+
}
9194
return CxxSpanReturnThunkBuilder(base: base, signature: funcDecl.signature,
9295
typeMappings: typeMappings, node: original)
9396
case .self:
@@ -125,7 +128,7 @@ struct CountedBy: ParamInfo {
125128
return CountedOrSizedReturnPointerThunkBuilder(
126129
base: base, countExpr: count,
127130
signature: funcDecl.signature,
128-
nonescaping: nonescaping, isSizedBy: sizedBy)
131+
nonescaping: nonescaping, isSizedBy: sizedBy, dependencies: dependencies)
129132
case .self:
130133
return base
131134
}
@@ -184,11 +187,13 @@ func getTypeName(_ type: TypeSyntax) throws -> TokenSyntax {
184187
}
185188
}
186189

187-
func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) -> TypeSyntax {
190+
func replaceTypeName(_ type: TypeSyntax, _ name: TokenSyntax) throws -> TypeSyntax {
188191
if let memberType = type.as(MemberTypeSyntax.self) {
189192
return TypeSyntax(memberType.with(\.name, name))
190193
}
191-
let idType = type.as(IdentifierTypeSyntax.self)!
194+
guard let idType = type.as(IdentifierTypeSyntax.self) else {
195+
throw DiagnosticError("unexpected type \(type) with kind \(type.kind)", node: type)
196+
}
192197
return TypeSyntax(idType.with(\.name, name))
193198
}
194199

@@ -264,6 +269,10 @@ func transformType(_ prev: TypeSyntax, _ generateSpan: Bool, _ isSizedBy: Bool)
264269
if let impOptType = prev.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
265270
return try transformType(impOptType.wrappedType, generateSpan, isSizedBy)
266271
}
272+
if let attrType = prev.as(AttributedTypeSyntax.self) {
273+
return TypeSyntax(
274+
attrType.with(\.baseType, try transformType(attrType.baseType, generateSpan, isSizedBy)))
275+
}
267276
let name = try getTypeName(prev)
268277
let text = name.text
269278
let isRaw = isRawPointerType(text: text)
@@ -283,7 +292,7 @@ func transformType(_ prev: TypeSyntax, _ generateSpan: Bool, _ isSizedBy: Bool)
283292
if isSizedBy {
284293
return TypeSyntax(IdentifierTypeSyntax(name: token))
285294
}
286-
return replaceTypeName(prev, token)
295+
return try replaceTypeName(prev, token)
287296
}
288297

289298
protocol BoundsCheckedThunkBuilder {
@@ -485,8 +494,9 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder {
485494
public let signature: FunctionSignatureSyntax
486495
public let nonescaping: Bool
487496
public let isSizedBy: Bool
497+
public let dependencies: [LifetimeDependence]
488498

489-
var generateSpan: Bool = false // needs more lifetime information
499+
var generateSpan: Bool { !dependencies.isEmpty }
490500

491501
var oldType: TypeSyntax {
492502
return signature.returnClause!.type
@@ -504,9 +514,14 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder {
504514

505515
func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax]) throws -> ExprSyntax {
506516
let call = try base.buildFunctionCall(pointerArgs)
517+
let startLabel = if generateSpan {
518+
"_unsafeStart"
519+
} else {
520+
"start"
521+
}
507522
return
508523
"""
509-
\(raw: try newType)(start: \(call), count: Int(\(countExpr)))
524+
\(raw: try newType)(\(raw: startLabel): \(call), count: Int(\(countExpr)))
510525
"""
511526
}
512527
}
@@ -1102,6 +1117,11 @@ public struct SwiftifyImportMacro: PeerMacro {
11021117
}
11031118
}
11041119
try checkArgs(parsedArgs, funcDecl)
1120+
parsedArgs.sort { a, b in
1121+
// make sure return value cast to Span happens last so that withUnsafeBufferPointer
1122+
// doesn't return a ~Escapable type
1123+
(a.pointerIndex != .return && b.pointerIndex == .return) || paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
1124+
}
11051125
let baseBuilder = FunctionCallBuilder(funcDecl)
11061126

11071127
let skipTrivialCount = hasTrivialCountVariants(parsedArgs)

test/Interop/Cxx/stdlib/Inputs/std-span.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,8 @@ inline void mixedFuncWithSafeWrapper5(ConstSpanOfInt s,
109109
inline void mixedFuncWithSafeWrapper6(ConstSpanOfInt s,
110110
int * __counted_by(len) p, int len) {}
111111

112+
inline ConstSpanOfInt mixedFuncWithSafeWrapper7(const int * __counted_by(len) p, int len) {
113+
return ConstSpanOfInt(p, len);
114+
}
115+
112116
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SPAN_H

test/Interop/Cxx/stdlib/std-span-interface.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ import CxxStdlib
3535
// CHECK-NEXT: @lifetime(p)
3636
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper1(_ p: Span<Int32>) -> Span<CInt>
3737
// CHECK-NEXT: @lifetime(borrow v)
38-
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func mixedFuncWithSafeWrapper2(_ v: borrowing VecOfInt, _ len: Int32) -> UnsafeBufferPointer<Int32>
38+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func mixedFuncWithSafeWrapper2(_ v: borrowing VecOfInt, _ len: Int32) -> Span<Int32>
3939
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper3(_ s: Span<CInt>, _ p: UnsafeMutableBufferPointer<Int32>)
4040
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper4(_ s: Span<CInt>, _ p: Span<Int32>)
4141
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper5(_ s: ConstSpanOfInt, _ p: Span<Int32>)
4242
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper6(_ s: ConstSpanOfInt, _ p: UnsafeMutableBufferPointer<Int32>)
43+
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper7(_ p: UnsafeBufferPointer<Int32>) -> ConstSpanOfInt

test/Macros/SwiftifyImport/CountedBy/PointerReturn.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// REQUIRES: swift_swift_parser
22

3-
// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s
3+
// RUN: %target-swift-frontend %s -swift-version 5 -module-name main -disable-availability-checking -typecheck -plugin-path %swift-plugin-dir -enable-experimental-feature LifetimeDependence -dump-macro-expansions 2>&1 | %FileCheck --match-full-lines %s
44

55
@_SwiftifyImport(.countedBy(pointer: .return, count: "len"))
66
func myFunc(_ len: CInt) -> UnsafeMutablePointer<CInt> {
@@ -10,6 +10,14 @@ func myFunc(_ len: CInt) -> UnsafeMutablePointer<CInt> {
1010
func nonEscaping(_ len: CInt) -> UnsafePointer<CInt> {
1111
}
1212

13+
@_SwiftifyImport(.countedBy(pointer: .return, count: "len2"), .countedBy(pointer: .param(1), count: "len1"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .copy))
14+
func lifetimeDependentCopy(_ p: UnsafePointer<CInt>, _ len1: CInt, _ len2: CInt) -> UnsafePointer<CInt> {
15+
}
16+
17+
@_SwiftifyImport(.countedBy(pointer: .return, count: "len2"), .countedBy(pointer: .param(1), count: "len1"), .lifetimeDependence(dependsOn: .param(1), pointer: .return, type: .borrow))
18+
func lifetimeDependentBorrow(_ p: borrowing UnsafePointer<CInt>, _ len1: CInt, _ len2: CInt) -> UnsafePointer<CInt> {
19+
}
20+
1321
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
1422
// CHECK-NEXT: func myFunc(_ len: CInt) -> UnsafeMutableBufferPointer<CInt> {
1523
// CHECK-NEXT: return UnsafeMutableBufferPointer<CInt> (start: myFunc(len), count: Int(len))
@@ -18,5 +26,16 @@ func nonEscaping(_ len: CInt) -> UnsafePointer<CInt> {
1826
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
1927
// CHECK-NEXT: func nonEscaping(_ len: CInt) -> UnsafeBufferPointer<CInt> {
2028
// CHECK-NEXT: return UnsafeBufferPointer<CInt> (start: nonEscaping(len), count: Int(len))
29+
30+
// CHECK: @_alwaysEmitIntoClient @lifetime(p)
31+
// CHECK-NEXT: func lifetimeDependentCopy(_ p: Span<CInt>, _ len2: CInt) -> Span<CInt> {
32+
// CHECK-NEXT: return Span<CInt> (_unsafeStart: p.withUnsafeBufferPointer { _pPtr in
33+
// CHECK-NEXT: return lifetimeDependentCopy(_pPtr.baseAddress!, CInt(exactly: p.count)!, len2)
34+
// CHECK-NEXT: }, count: Int(len2))
35+
// CHECK-NEXT: }
36+
37+
// CHECK: @_alwaysEmitIntoClient @lifetime(borrow p)
38+
// CHECK-NEXT: func lifetimeDependentBorrow(_ p: borrowing UnsafeBufferPointer<CInt>, _ len2: CInt) -> Span<CInt> {
39+
// CHECK-NEXT: return Span<CInt> (_unsafeStart: lifetimeDependentBorrow(p.baseAddress!, CInt(exactly: p.count)!, len2), count: Int(len2))
2140
// CHECK-NEXT: }
2241

0 commit comments

Comments
 (0)