Skip to content

Commit 2913255

Browse files
authored
[Swiftify] Fix __sized_by and nullable return values (#81693) (#81725)
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 (cherry picked from commit 526c683)
1 parent 7077ef5 commit 2913255

File tree

9 files changed

+392
-22
lines changed

9 files changed

+392
-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,3 +14,11 @@ 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+
}
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: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
15+
// CHECK-NEXT: @lifetime(copy p)
16+
// CHECK-NEXT: @lifetime(p: copy p)
17+
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int32, _ offset: Int32, _ len2: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
18+
19+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
20+
// CHECK-NEXT: @lifetime(borrow p)
21+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func noncountedLifetime(_ len: Int32, _ p: UnsafeMutablePointer<Int32>!) -> MutableSpan<Int32>
22+
23+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
24+
// CHECK-NEXT: @lifetime(copy p)
25+
// CHECK-NEXT: @lifetime(p: copy p)
26+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
27+
28+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
29+
// CHECK-NEXT: @lifetime(copy p)
30+
// CHECK-NEXT: @lifetime(p: copy p)
31+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
32+
33+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
34+
// CHECK-NEXT: @lifetime(copy p)
35+
// CHECK-NEXT: @lifetime(p: copy p)
36+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ len: Int32, _ p: inout MutableSpan<Int32>?) -> MutableSpan<Int32>?
37+
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 shared(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
42+
43+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
44+
// CHECK-NEXT: @lifetime(copy p)
45+
// CHECK-NEXT: @lifetime(p: copy p)
46+
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ len: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
47+
48+
49+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
50+
@inlinable
51+
public func callComplexExpr(_ p: inout MutableSpan<CInt>) {
52+
let _: MutableSpan<CInt> = complexExpr(73, 37, 42, &p)
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 callNonnull(_ p: inout MutableSpan<CInt>) {
58+
let _: MutableSpan<CInt> = nonnull(73, &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 callNullUnspecified(_ p: inout MutableSpan<CInt>) {
64+
let _: MutableSpan<CInt> = nullUnspecified(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 callNullable(_ p: inout MutableSpan<CInt>?) {
70+
let _: MutableSpan<CInt> = nullable(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 callShared(_ p: inout MutableSpan<CInt>) {
76+
let _: MutableSpan<CInt> = shared(CInt(p.count), &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 callSimple(_ p: inout MutableSpan<CInt>) {
82+
let _: MutableSpan<CInt> = simple(73, &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 callNoncountedLifetime(_ p: UnsafeMutablePointer<CInt>) {
88+
let _: MutableSpan<CInt> = noncountedLifetime(73, p)
89+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
15+
// CHECK-NEXT: @lifetime(copy p)
16+
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int32, _ offset: Int32, _ len2: Int32, _ p: RawSpan) -> RawSpan
17+
18+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
19+
// CHECK-NEXT: @lifetime(copy p)
20+
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ len: Int32, _ p: RawSpan) -> RawSpan
21+
22+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
23+
// CHECK-NEXT: @lifetime(borrow p)
24+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func nonsizedLifetime(_ len: Int32, _ p: UnsafeRawPointer!) -> RawSpan
25+
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: @_alwaysEmitIntoClient public func nullUnspecified(_ len: Int32, _ p: RawSpan) -> RawSpan
29+
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 nullable(_ len: Int32, _ p: RawSpan?) -> RawSpan?
33+
34+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
35+
// CHECK-NEXT: @lifetime(copy p)
36+
// CHECK-NEXT: @_alwaysEmitIntoClient public func opaque(_ len: Int32, _ p: RawSpan) -> RawSpan
37+
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: @_alwaysEmitIntoClient public func shared(_ len: Int32, _ p: RawSpan) -> RawSpan
41+
42+
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
43+
// CHECK-NEXT: @lifetime(copy p)
44+
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ len: Int32, _ p: RawSpan) -> RawSpan
45+
46+
47+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
48+
@inlinable
49+
public func callComplexExpr(_ p: RawSpan) {
50+
let _: RawSpan = complexExpr(73, 37, 42, p)
51+
}
52+
53+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
54+
@inlinable
55+
public func callNonnull(_ p: RawSpan) {
56+
let _: RawSpan = nonnull(73, p)
57+
}
58+
59+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
60+
@inlinable
61+
public func callNullUnspecified(_ p: RawSpan) {
62+
let _: RawSpan = nullUnspecified(73, p)
63+
}
64+
65+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
66+
@inlinable
67+
public func callNullable(_ p: RawSpan?) {
68+
let _: RawSpan = nullable(73, p)!
69+
}
70+
71+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
72+
@inlinable
73+
public func callShared(_ p: RawSpan) {
74+
let _: RawSpan = shared(CInt(p.byteCount), p)
75+
}
76+
77+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
78+
@inlinable
79+
public func callSimple(_ p: RawSpan) {
80+
let _: RawSpan = simple(73, p)
81+
}
82+
83+
@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
84+
@inlinable
85+
public func callNonsizedLifetime(_ p: UnsafeRawPointer) {
86+
let _: RawSpan = nonsizedLifetime(73, p)
87+
}

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)