Skip to content

Commit 526c683

Browse files
authored
[Swiftify] Fix __sized_by and nullable return values (#81693)
Nullable return Spans did not include __swiftifyOverrideLifetime, resulting in a lifetime error when returning the Span. Meanwhile return values for __sized_by did not use the correct label for the call to the RawSpan initializer, using `count` instead of `byteCount`. rdar://151804085 rdar://151799287
1 parent 789f657 commit 526c683

File tree

9 files changed

+407
-22
lines changed

9 files changed

+407
-22
lines changed

lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ extension PointerBoundsThunkBuilder {
643643
return try transformType(oldType, generateSpan, isSizedBy, isParameter)
644644
}
645645
}
646+
var countLabel: String {
647+
return isSizedBy && generateSpan ? "byteCount" : "count"
648+
}
646649
}
647650

648651
protocol ParamBoundsThunkBuilder: BoundsThunkBuilder {
@@ -699,26 +702,28 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder {
699702
"start"
700703
}
701704
var cast = try newType
705+
var expr: ExprSyntax
702706
if nullable {
703707
if let optType = cast.as(OptionalTypeSyntax.self) {
704708
cast = optType.wrappedType
705709
}
706-
return """
707-
{ () in
708-
let _resultValue = \(call)
709-
if unsafe _resultValue == nil {
710-
return nil
711-
} else {
712-
return unsafe \(raw: cast)(\(raw: startLabel): _resultValue!, count: Int(\(countExpr)))
713-
}
714-
}()
715-
"""
710+
expr =
711+
"""
712+
{ () in
713+
let _resultValue = \(call)
714+
if unsafe _resultValue == nil {
715+
return nil
716+
} else {
717+
return unsafe _swiftifyOverrideLifetime(\(raw: cast)(\(raw: startLabel): _resultValue!, \(raw: countLabel): Int(\(countExpr))), copying: ())
718+
}
719+
}()
720+
"""
721+
} else {
722+
expr =
723+
"""
724+
\(raw: cast)(\(raw: startLabel): \(call), \(raw: countLabel): Int(\(countExpr)))
725+
"""
716726
}
717-
var expr = ExprSyntax(
718-
"""
719-
\(raw: cast)(\(raw: startLabel): \(call), count: Int(\(countExpr)))
720-
"""
721-
)
722727
if generateSpan {
723728
expr = "_swiftifyOverrideLifetime(\(expr), copying: ())"
724729
}
@@ -823,11 +828,10 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
823828
}
824829

825830
func getCount() -> ExprSyntax {
826-
let countName = isSizedBy && generateSpan ? "byteCount" : "count"
827831
if nullable {
828-
return ExprSyntax("\(name)?.\(raw: countName) ?? 0")
832+
return ExprSyntax("\(name)?.\(raw: countLabel) ?? 0")
829833
}
830-
return ExprSyntax("\(name).\(raw: countName)")
834+
return ExprSyntax("\(name).\(raw: countLabel)")
831835
}
832836

833837
func peelOptionalType(_ type: TypeSyntax) -> TypeSyntax {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#define __counted_by(x) __attribute__((__counted_by__(x)))
4+
#define __lifetimebound __attribute__((lifetimebound))
5+
6+
int * __counted_by(len) simple(int len, int len2, int * __counted_by(len2) __lifetimebound p);
7+
8+
int * __counted_by(len) shared(int len, int * __counted_by(len) __lifetimebound p);
9+
10+
int * __counted_by(len - offset) complexExpr(int len, int offset, int len2, int * __counted_by(len2) __lifetimebound p);
11+
12+
int * __counted_by(len) _Null_unspecified nullUnspecified(int len, int len2, int * __counted_by(len2) __lifetimebound _Null_unspecified p);
13+
14+
int * __counted_by(len) _Nonnull nonnull(int len, int len2, int * __counted_by(len2) __lifetimebound _Nonnull p);
15+
16+
int * __counted_by(len) _Nullable nullable(int len, int len2, int * __counted_by(len2) __lifetimebound _Nullable p);
17+
18+
typedef struct foo opaque_t;
19+
opaque_t * __counted_by(len) opaque(int len, int len2, opaque_t * __counted_by(len2) __lifetimebound p);
20+
21+
int * __counted_by(len) noncountedLifetime(int len, int * __lifetimebound p);

test/Interop/C/swiftify-import/Inputs/module.modulemap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ module SizedByNoEscapeClang {
1414
header "sized-by-noescape.h"
1515
export *
1616
}
17+
module SizedByLifetimeboundClang {
18+
header "sized-by-lifetimebound.h"
19+
export *
20+
}
21+
module CountedByLifetimeboundClang {
22+
header "counted-by-lifetimebound.h"
23+
export *
24+
}
1725
module CommentsClang {
1826
header "comments.h"
1927
export *
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#define __sized_by(x) __attribute__((__sized_by__(x)))
4+
#define __lifetimebound __attribute__((lifetimebound))
5+
6+
const void * __sized_by(len) simple(int len, int len2, const void * __sized_by(len2) __lifetimebound p);
7+
8+
const void * __sized_by(len) shared(int len, const void * __sized_by(len) __lifetimebound p);
9+
10+
const void * __sized_by(len - offset) complexExpr(int len, int offset, int len2, const void * __sized_by(len2) __lifetimebound p);
11+
12+
const void * __sized_by(len) _Null_unspecified nullUnspecified(int len, int len2, const void * __sized_by(len2) __lifetimebound _Null_unspecified p);
13+
14+
const void * __sized_by(len) _Nonnull nonnull(int len, int len2, const void * __sized_by(len2) __lifetimebound _Nonnull p);
15+
16+
const void * __sized_by(len) _Nullable nullable(int len, int len2, const void * __sized_by(len2) __lifetimebound _Nullable p);
17+
18+
typedef struct foo opaque_t;
19+
opaque_t * __sized_by(len) opaque(int len, int len2, opaque_t * __sized_by(len2) __lifetimebound p);
20+
21+
const void * __sized_by(len) nonsizedLifetime(int len, const void * __lifetimebound p);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
// REQUIRES: swift_feature_LifetimeDependence
3+
4+
// RUN: %target-swift-ide-test -print-module -module-to-print=CountedByLifetimeboundClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s
5+
6+
// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well
7+
// RUN: %empty-directory(%t)
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByLifetimebound.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %s
9+
10+
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __sized_by __lifetimebound parameters and return values.
11+
12+
import CountedByLifetimeboundClang
13+
14+
// CHECK: /// This is an auto-generated wrapper for safer interop
15+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
16+
// CHECK-NEXT: @lifetime(copy p)
17+
// CHECK-NEXT: @lifetime(p: copy p)
18+
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int32, _ offset: Int32, _ len2: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
19+
20+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
21+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
22+
// CHECK-NEXT: @lifetime(borrow p)
23+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func noncountedLifetime(_ len: Int32, _ p: UnsafeMutablePointer<Int32>!) -> MutableSpan<Int32>
24+
25+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
26+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
27+
// CHECK-NEXT: @lifetime(copy p)
28+
// CHECK-NEXT: @lifetime(p: copy p)
29+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
30+
31+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
32+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
33+
// CHECK-NEXT: @lifetime(copy p)
34+
// CHECK-NEXT: @lifetime(p: copy p)
35+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
36+
37+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
38+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
39+
// CHECK-NEXT: @lifetime(copy p)
40+
// CHECK-NEXT: @lifetime(p: copy p)
41+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ len: Int32, _ p: inout MutableSpan<Int32>?) -> MutableSpan<Int32>?
42+
43+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
44+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
45+
// CHECK-NEXT: @lifetime(copy p)
46+
// CHECK-NEXT: @lifetime(p: copy p)
47+
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
48+
49+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
50+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
51+
// CHECK-NEXT: @lifetime(copy p)
52+
// CHECK-NEXT: @lifetime(p: copy p)
53+
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
54+
55+
56+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
57+
@inlinable
58+
public func callComplexExpr(_ p: inout MutableSpan<CInt>) {
59+
let _: MutableSpan<CInt> = complexExpr(73, 37, 42, &p)
60+
}
61+
62+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
63+
@inlinable
64+
public func callNonnull(_ p: inout MutableSpan<CInt>) {
65+
let _: MutableSpan<CInt> = nonnull(73, &p)
66+
}
67+
68+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
69+
@inlinable
70+
public func callNullUnspecified(_ p: inout MutableSpan<CInt>) {
71+
let _: MutableSpan<CInt> = nullUnspecified(73, &p)
72+
}
73+
74+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
75+
@inlinable
76+
public func callNullable(_ p: inout MutableSpan<CInt>?) {
77+
let _: MutableSpan<CInt> = nullable(73, &p)!
78+
}
79+
80+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
81+
@inlinable
82+
public func callShared(_ p: inout MutableSpan<CInt>) {
83+
let _: MutableSpan<CInt> = shared(CInt(p.count), &p)
84+
}
85+
86+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
87+
@inlinable
88+
public func callSimple(_ p: inout MutableSpan<CInt>) {
89+
let _: MutableSpan<CInt> = simple(73, &p)
90+
}
91+
92+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
93+
@inlinable
94+
public func callNoncountedLifetime(_ p: UnsafeMutablePointer<CInt>) {
95+
let _: MutableSpan<CInt> = noncountedLifetime(73, p)
96+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
// REQUIRES: swift_feature_LifetimeDependence
3+
4+
// RUN: %target-swift-ide-test -print-module -module-to-print=SizedByLifetimeboundClang -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s
5+
6+
// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well
7+
// RUN: %empty-directory(%t)
8+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/SizedByLifetimebound.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %s
9+
10+
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __sized_by __lifetimebound parameters and return values.
11+
12+
import SizedByLifetimeboundClang
13+
14+
// CHECK: /// This is an auto-generated wrapper for safer interop
15+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
16+
// CHECK-NEXT: @lifetime(copy p)
17+
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int32, _ offset: Int32, _ len2: Int32, _ p: RawSpan) -> RawSpan
18+
19+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
20+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
21+
// CHECK-NEXT: @lifetime(copy p)
22+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ len: Int32, _ p: RawSpan) -> RawSpan
23+
24+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
25+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
26+
// CHECK-NEXT: @lifetime(borrow p)
27+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func nonsizedLifetime(_ len: Int32, _ p: UnsafeRawPointer!) -> RawSpan
28+
29+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
30+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
31+
// CHECK-NEXT: @lifetime(copy p)
32+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ len: Int32, _ p: RawSpan) -> RawSpan
33+
34+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
35+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
36+
// CHECK-NEXT: @lifetime(copy p)
37+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ len: Int32, _ p: RawSpan?) -> RawSpan?
38+
39+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
40+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
41+
// CHECK-NEXT: @lifetime(copy p)
42+
// CHECK-NEXT: @_alwaysEmitIntoClient public func opaque(_ len: Int32, _ p: RawSpan) -> RawSpan
43+
44+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
45+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
46+
// CHECK-NEXT: @lifetime(copy p)
47+
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int32, _ p: RawSpan) -> RawSpan
48+
49+
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
50+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
51+
// CHECK-NEXT: @lifetime(copy p)
52+
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ len: Int32, _ p: RawSpan) -> RawSpan
53+
54+
55+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
56+
@inlinable
57+
public func callComplexExpr(_ p: RawSpan) {
58+
let _: RawSpan = complexExpr(73, 37, 42, p)
59+
}
60+
61+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
62+
@inlinable
63+
public func callNonnull(_ p: RawSpan) {
64+
let _: RawSpan = nonnull(73, p)
65+
}
66+
67+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
68+
@inlinable
69+
public func callNullUnspecified(_ p: RawSpan) {
70+
let _: RawSpan = nullUnspecified(73, p)
71+
}
72+
73+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
74+
@inlinable
75+
public func callNullable(_ p: RawSpan?) {
76+
let _: RawSpan = nullable(73, p)!
77+
}
78+
79+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
80+
@inlinable
81+
public func callShared(_ p: RawSpan) {
82+
let _: RawSpan = shared(CInt(p.byteCount), p)
83+
}
84+
85+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
86+
@inlinable
87+
public func callSimple(_ p: RawSpan) {
88+
let _: RawSpan = simple(73, p)
89+
}
90+
91+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
92+
@inlinable
93+
public func callNonsizedLifetime(_ p: UnsafeRawPointer) {
94+
let _: RawSpan = nonsizedLifetime(73, p)
95+
}

test/Macros/SwiftifyImport/CountedBy/Nullable.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func myFunc4(_ ptr: UnsafeMutablePointer<CInt>?, _ len: CInt) -> UnsafeMutablePo
7171
// CHECK-NEXT: if ptr?.count ?? 0 < _ptrCount || _ptrCount < 0 {
7272
// CHECK-NEXT: fatalError("bounds check failure when calling unsafe function")
7373
// CHECK-NEXT: }
74-
// CHECK-NEXT: return { () in
74+
// CHECK-NEXT: return unsafe _swiftifyOverrideLifetime({ () in
7575
// CHECK-NEXT: let _resultValue = { () in
7676
// CHECK-NEXT: return if ptr == nil {
7777
// CHECK-NEXT: unsafe myFunc4(nil, len)
@@ -84,7 +84,7 @@ func myFunc4(_ ptr: UnsafeMutablePointer<CInt>?, _ len: CInt) -> UnsafeMutablePo
8484
// CHECK-NEXT: if unsafe _resultValue == nil {
8585
// CHECK-NEXT: return nil
8686
// CHECK-NEXT: } else {
87-
// CHECK-NEXT: return unsafe MutableSpan<CInt>(_unsafeStart: _resultValue!, count: Int(len))
87+
// CHECK-NEXT: return unsafe _swiftifyOverrideLifetime(MutableSpan<CInt>(_unsafeStart: _resultValue!, count: Int(len)), copying: ())
8888
// CHECK-NEXT: }
89-
// CHECK-NEXT: }()
90-
// CHECK-NEXT: }
89+
// CHECK-NEXT: }(), copying: ())
90+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)