Skip to content

Commit 262a53f

Browse files
authored
[Swiftify] Update availability for CxxSpan<->Span, fix lifetimebound on parameters with reference type (#81634)
Update availability for CxxSpan<->Span, fix lifetimebound on parameters with reference type Because swift-ide-test doesn't care about typechecking, std-span-interface.swift passed despite containing 2 separate errors. This updates the test file to properly exercise the entire compilation pipeline for the macro expansions, by running swift-frontend -emit-module and calling each macro expansion. The first issue was that CxxSpan initializers taking [Mutable]Span still had their availability set to Swift 6.2+, even after back-deploying caused [Mutable]Span to have availability back to Swift 5.0. Since _SwiftifyImport expansions copy the availbility of Span, this resulted in the macro expansions calling unavailable initializers. Interestingly enough, this manifested itself in the form of a tripped assert in SIL verification, because although we do now typecheck the expansions from _SwiftifyImport, the compilation can still keep going after `shouldEmitFunctionBody` returns false: the macro expansion declaration is still there, but is now missing its definition, despite not being external. The second issue was when parameters with C++ reference types were annotated with `[[clang::lifetimebound]]`. For parameters with a type that is `Escapable`, this is normally done using `@lifetime(borrow foo)`. However C++ reference parameters are imported as `inout`, which requires the `@lifetime(&foo)` syntax. rdar://151493400 rdar://151678415
1 parent 526c683 commit 262a53f

File tree

4 files changed

+127
-13
lines changed

4 files changed

+127
-13
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9225,9 +9225,10 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
92259225
paramHasLifetimeInfo = true;
92269226
}
92279227
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
9228-
printer.printLifetimeboundReturn(
9229-
index, !paramHasBoundsInfo &&
9230-
swiftParamTy->isEscapable());
9228+
// If this parameter has bounds info we will tranform it into a Span,
9229+
// so then it will no longer be Escapable.
9230+
bool willBeEscapable = swiftParamTy->isEscapable() && !paramHasBoundsInfo;
9231+
printer.printLifetimeboundReturn(index, willBeEscapable);
92319232
paramHasLifetimeInfo = true;
92329233
returnHasLifetimeInfo = true;
92339234
}

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,18 @@ func setLifetimeDependencies(
13061306
}
13071307
}
13081308

1309+
func isInout(_ type: TypeSyntax) -> Bool {
1310+
guard let attr = type.as(AttributedTypeSyntax.self) else {
1311+
return false
1312+
}
1313+
return attr.specifiers.contains(where: { e in
1314+
guard let simpleSpec = e.as(SimpleTypeSpecifierSyntax.self) else {
1315+
return false
1316+
}
1317+
return simpleSpec.specifier.text == "inout"
1318+
})
1319+
}
1320+
13091321
func getReturnLifetimeAttribute(
13101322
_ funcDecl: FunctionDeclSyntax,
13111323
_ dependencies: [SwiftifyExpr: [LifetimeDependence]]
@@ -1318,10 +1330,14 @@ func getReturnLifetimeAttribute(
13181330
for dependence in returnDependencies {
13191331
switch dependence.type {
13201332
case .borrow:
1321-
args.append(
1322-
LabeledExprSyntax(
1323-
expression:
1324-
DeclReferenceExprSyntax(baseName: TokenSyntax("borrow"))))
1333+
if isInout(getSwiftifyExprType(funcDecl, dependence.dependsOn)) {
1334+
args.append(LabeledExprSyntax(expression: ExprSyntax("&")))
1335+
} else {
1336+
args.append(
1337+
LabeledExprSyntax(
1338+
expression:
1339+
DeclReferenceExprSyntax(baseName: TokenSyntax("borrow"))))
1340+
}
13251341
case .copy:
13261342
args.append(
13271343
LabeledExprSyntax(

stdlib/public/Cxx/CxxSpan.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ extension CxxSpan {
8888
unsafe self.init(unsafeMutableBufferPointer.baseAddress!, Size(unsafeMutableBufferPointer.count))
8989
}
9090

91-
@available(SwiftStdlib 6.2, *)
91+
@available(SwiftCompatibilitySpan 5.0, *)
9292
@inlinable
9393
@unsafe
9494
public init(_ span: Span<Element>) {
@@ -99,7 +99,7 @@ extension CxxSpan {
9999
}
100100
}
101101

102-
@available(SwiftStdlib 6.2, *)
102+
@available(SwiftCompatibilitySpan 5.0, *)
103103
extension Span {
104104
@_alwaysEmitIntoClient
105105
@unsafe
@@ -115,7 +115,7 @@ extension Span {
115115
}
116116
}
117117

118-
@available(SwiftStdlib 6.2, *)
118+
@available(SwiftCompatibilitySpan 5.0, *)
119119
extension MutableSpan {
120120
@_alwaysEmitIntoClient
121121
@unsafe
@@ -151,7 +151,7 @@ extension CxxMutableSpan {
151151
unsafe self.init(unsafeMutableBufferPointer.baseAddress!, Size(unsafeMutableBufferPointer.count))
152152
}
153153

154-
@available(SwiftStdlib 6.2, *)
154+
@available(SwiftCompatibilitySpan 5.0, *)
155155
@inlinable
156156
@unsafe
157157
public init(_ span: consuming MutableSpan<Element>) {

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

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
// RUN: %target-swift-ide-test -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -print-module -module-to-print=StdSpan -source-filename=x -enable-experimental-cxx-interop -Xcc -std=c++20 -module-cache-path %t > %t/interface.swift
33
// RUN: %FileCheck %s < %t/interface.swift
44

5+
// Make sure we trigger typechecking and SIL diagnostics
6+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -cxx-interoperability-mode=default -strict-memory-safety -warnings-as-errors -Xcc -std=c++20 %s
7+
58
// REQUIRES: swift_feature_SafeInteropWrappers
9+
// REQUIRES: swift_feature_LifetimeDependence
610

711
// FIXME swift-ci linux tests do not support std::span
812
// UNSUPPORTED: OS=linux-gnu, OS=linux-android, OS=linux-androideabi
@@ -54,7 +58,7 @@ import CxxStdlib
5458

5559
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
5660
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
57-
// CHECK-NEXT: @lifetime(borrow v)
61+
// CHECK-NEXT: @lifetime(&v)
5862
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func FuncWithMutableSafeWrapper3(_ v: inout VecOfInt) -> MutableSpan<CInt>
5963

6064
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
@@ -65,7 +69,7 @@ import CxxStdlib
6569

6670
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
6771
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
68-
// CHECK-NEXT: @lifetime(borrow v)
72+
// CHECK-NEXT: @lifetime(&v)
6973
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func MixedFuncWithMutableSafeWrapper2(_ v: inout VecOfInt, _ len: Int32) -> MutableSpan<Int32>
7074

7175
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
@@ -132,3 +136,96 @@ import CxxStdlib
132136

133137
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
134138
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper7(_ p: UnsafeBufferPointer<Int32>) -> ConstSpanOfInt
139+
140+
func callMethodWithSafeWrapper(_ x: inout X, s: Span<CInt>) {
141+
x.methodWithSafeWrapper(s)
142+
}
143+
144+
func callFooBar(_ x: inout SpanWithoutTypeAlias, _ s: ConstSpanOfInt) {
145+
let _: Span<CInt> = x.bar()
146+
unsafe x.foo(s)
147+
}
148+
149+
@lifetime(span: copy span)
150+
func callFuncWithMutableSafeWrapper(_ span: inout MutableSpan<CInt>, ) {
151+
FuncWithMutableSafeWrapper(&span)
152+
}
153+
154+
@lifetime(span: copy span)
155+
func callFuncWithMutableSafeWrapper2(_ span: inout MutableSpan<CInt>, ) {
156+
let _: MutableSpan<CInt> = FuncWithMutableSafeWrapper2(&span)
157+
}
158+
159+
@lifetime(span: copy span)
160+
func callMixedFuncWithMutableSafeWrapper1(_ span: inout MutableSpan<CInt>, ) {
161+
let _: MutableSpan<CInt> = MixedFuncWithMutableSafeWrapper1(&span)
162+
}
163+
164+
func MixedFuncWithMutableSafeWrapper2(_ v: VecOfInt) {
165+
var v2 = v
166+
let _ = MixedFuncWithMutableSafeWrapper2(&v2, 37)
167+
}
168+
169+
@lifetime(span: copy span)
170+
func callMixedFuncWithMutableSafeWrapper3(_ span: inout MutableSpan<CInt>, _ p: UnsafeMutableBufferPointer<CInt>) {
171+
unsafe MixedFuncWithMutableSafeWrapper3(&span, p)
172+
}
173+
174+
@lifetime(span1: copy span2)
175+
@lifetime(span2: copy span2)
176+
func callMixedFuncWithMutableSafeWrapper4(_ span1: inout MutableSpan<CInt>, _ span2: inout MutableSpan<CInt>) {
177+
MixedFuncWithMutableSafeWrapper4(&span1, &span2)
178+
}
179+
180+
@lifetime(span: copy span)
181+
func callMixedFuncWithMutableSafeWrapper5(_ span: inout MutableSpan<CInt>, _ s: SpanOfInt) {
182+
unsafe MixedFuncWithMutableSafeWrapper5(s, &span)
183+
}
184+
185+
func callMixedFuncWithMutableSafeWrapper6(_ s: SpanOfInt, _ p: UnsafeMutableBufferPointer<CInt>) {
186+
unsafe MixedFuncWithMutableSafeWrapper6(s, p)
187+
}
188+
189+
func callMixedFuncWithMutableSafeWrapper7(_ p: UnsafeMutableBufferPointer<CInt>) {
190+
let _ = unsafe MixedFuncWithMutableSafeWrapper7(p)
191+
}
192+
193+
func callFuncWithSafeWrapper(_ s: Span<CInt>) {
194+
funcWithSafeWrapper(s)
195+
}
196+
197+
func callFuncWithSafeWrapper2(_ s: Span<CInt>) {
198+
let _ = funcWithSafeWrapper2(s)
199+
}
200+
201+
func callFuncWithSafeWrapper3(_ v: borrowing VecOfInt) {
202+
let _: Span<CInt> = funcWithSafeWrapper3(v)
203+
}
204+
205+
func callMixedFuncWithSafeWrapper1(_ s: Span<CInt>) {
206+
let _: Span<CInt> = mixedFuncWithSafeWrapper1(s)
207+
}
208+
209+
func callMixedFuncWithSafeWrapper2(_ v: borrowing VecOfInt) {
210+
let _: Span<CInt> = mixedFuncWithSafeWrapper2(v, 73)
211+
}
212+
213+
func callMixedFuncWithSafeWrapper3(_ s: Span<CInt>, _ p: UnsafeMutableBufferPointer<CInt>) {
214+
unsafe mixedFuncWithSafeWrapper3(s, p)
215+
}
216+
217+
func callMixedFuncWithSafeWrapper4(_ s: Span<CInt>, _ s2: Span<CInt>) {
218+
mixedFuncWithSafeWrapper4(s, s2)
219+
}
220+
221+
func callMixedFuncWithSafeWrapper5(_ s: ConstSpanOfInt, _ s2: Span<CInt>) {
222+
unsafe mixedFuncWithSafeWrapper5(s, s2)
223+
}
224+
225+
func callMixedFuncWithSafeWrapper6(_ s: ConstSpanOfInt, _ p: UnsafeMutableBufferPointer<CInt>) {
226+
unsafe mixedFuncWithSafeWrapper6(s, p)
227+
}
228+
229+
func callMixedFuncWithSafeWrapper7(_ p: UnsafeBufferPointer<CInt>) {
230+
let _: ConstSpanOfInt = unsafe mixedFuncWithSafeWrapper7(p)
231+
}

0 commit comments

Comments
 (0)