Skip to content

Commit 0c3daab

Browse files
committed
Refactor C string conversion stuff (again)
1 parent f409d27 commit 0c3daab

File tree

2 files changed

+84
-108
lines changed

2 files changed

+84
-108
lines changed

Diff for: Sources/Testing/Expectations/ExpectationContext+Pointers.swift

+83-87
Original file line numberDiff line numberDiff line change
@@ -12,60 +12,49 @@
1212
// MARK: String-to-C-string handling and implicit pointer conversions
1313

1414
extension __ExpectationContext {
15-
/// A protocol describing types that can be implicitly cast to C strings or
16-
/// pointers when passed to C functions.
17-
///
18-
/// This protocol helps the compiler disambiguate string values when they need
19-
/// to be implicitly cast to C strings or other pointer types.
20-
///
21-
/// - Warning: This protocol is used to implement the `#expect()` and
22-
/// `#require()` macros. Do not use it directly. Do not add conformances to
23-
/// this protocol outside of the testing library.
24-
public protocol __ImplicitlyPointerConvertible {
25-
/// The concrete type of the resulting pointer when an instance of this type
26-
/// is implicitly cast.
27-
associatedtype __ImplicitPointerConversionResult
28-
29-
/// Perform an implicit cast of this instance to its corresponding pointer
30-
/// type.
31-
///
32-
/// - Parameters:
33-
/// - expectationContext: The expectation context that needs to cast this
34-
/// instance.
35-
///
36-
/// - Returns: A copy of this instance, cast to a pointer.
37-
///
38-
/// The implementation of this method should register the resulting pointer
39-
/// with `expectationContext` so that it is not leaked.
40-
///
41-
/// - Warning: This function is used to implement the `#expect()` and
42-
/// `#require()` macros. Do not call it directly.
43-
func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> __ImplicitPointerConversionResult
15+
/// Capture a pointer for use if the expectation currently being evaluated
16+
/// fails.
17+
///
18+
/// - Parameters:
19+
/// - value: The value to pass through.
20+
/// - id: A value that uniquely identifies the represented expression in the
21+
/// context of the expectation currently being evaluated.
22+
///
23+
/// - Returns: `value`, verbatim.
24+
///
25+
/// This overload of `callAsFunction()` is used when a pointer is passed to
26+
/// allow for the correct handling of implicit pointer conversion after it
27+
/// returns.
28+
///
29+
/// - Warning: This function is used to implement the `#expect()` and
30+
/// `#require()` macros. Do not call it directly.
31+
@inlinable public mutating func callAsFunction<P>(_ value: P, _ id: __ExpressionID) -> P where P: _Pointer {
32+
captureValue(value, id)
4433
}
4534

46-
/// Capture information about a value for use if the expectation currently
47-
/// being evaluated fails.
35+
/// Capture a pointer for use if the expectation currently being evaluated
36+
/// fails.
4837
///
4938
/// - Parameters:
5039
/// - value: The value to pass through.
5140
/// - id: A value that uniquely identifies the represented expression in the
5241
/// context of the expectation currently being evaluated.
5342
///
54-
/// - Returns: `value`, cast to a C string.
43+
/// - Returns: `value`, verbatim.
5544
///
56-
/// This overload of `callAsFunction(_:_:)` helps the compiler disambiguate
57-
/// string values when they need to be implicitly cast to C strings or other
58-
/// pointer types.
45+
/// This overload of `callAsFunction()` is used when a pointer is passed to
46+
/// allow for the correct handling of implicit pointer conversion after it
47+
/// returns.
5948
///
6049
/// - Warning: This function is used to implement the `#expect()` and
6150
/// `#require()` macros. Do not call it directly.
6251
@_disfavoredOverload
63-
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S.__ImplicitPointerConversionResult where S: __ImplicitlyPointerConvertible {
64-
captureValue(value, id).__implicitlyCast(for: &self)
52+
@inlinable public mutating func callAsFunction<P>(_ value: P?, _ id: __ExpressionID) -> P? where P: _Pointer {
53+
captureValue(value, id)
6554
}
6655

67-
/// Capture information about a value for use if the expectation currently
68-
/// being evaluated fails.
56+
/// Capture a string for use if the expectation currently being evaluated
57+
/// fails.
6958
///
7059
/// - Parameters:
7160
/// - value: The value to pass through.
@@ -74,73 +63,80 @@ extension __ExpectationContext {
7463
///
7564
/// - Returns: `value`, verbatim.
7665
///
77-
/// This overload of `callAsFunction(_:_:)` helps the compiler disambiguate
78-
/// string values when they do _not_ need to be implicitly cast to C strings
79-
/// or other pointer types. Without this overload, all instances of conforming
80-
/// types end up being cast to pointers before being compared (etc.), which
81-
/// produces incorrect results.
66+
/// This overload of `callAsFunction()` is used when a string is passed to
67+
/// allow for the correct handling of implicit C string conversion after it
68+
/// returns. For more information about implicit type conversions performed by
69+
/// the Swift compiler, see [here](https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter).
8270
///
8371
/// - Warning: This function is used to implement the `#expect()` and
8472
/// `#require()` macros. Do not call it directly.
85-
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S where S: __ImplicitlyPointerConvertible {
73+
@inlinable public mutating func callAsFunction(_ value: String, _ id: __ExpressionID) -> String {
8674
captureValue(value, id)
8775
}
8876

89-
/// Convert some pointer to another pointer type and capture information about
90-
/// it for use if the expectation currently being evaluated fails.
77+
/// Capture an optional string for use if the expectation currently being
78+
/// evaluated fails.
9179
///
9280
/// - Parameters:
93-
/// - value: The pointer to cast.
81+
/// - value: The value to pass through.
9482
/// - id: A value that uniquely identifies the represented expression in the
9583
/// context of the expectation currently being evaluated.
9684
///
97-
/// - Returns: `value`, cast to another type of pointer.
85+
/// - Returns: `value`, verbatim.
9886
///
99-
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
100-
/// between various pointer types that are normally provided by the compiler.
87+
/// This overload of `callAsFunction()` is used when an optional string is
88+
/// passed to allow for the correct handling of implicit C string conversion
89+
/// after it returns. For more information about implicit type conversions
90+
/// performed by the Swift compiler, see [here](https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter).
10191
///
10292
/// - Warning: This function is used to implement the `#expect()` and
10393
/// `#require()` macros. Do not call it directly.
104-
@inlinable public mutating func callAsFunction<P1, P2>(_ value: P1?, _ id: __ExpressionID) -> P2! where P1: _Pointer, P2: _Pointer {
105-
captureValue(value, id).flatMap { value in
106-
P2(bitPattern: Int(bitPattern: value))
107-
}
108-
}
109-
}
110-
111-
extension __ExpectationContext.__ImplicitlyPointerConvertible where Self: Collection {
112-
public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> UnsafeMutablePointer<Element> {
113-
// If `count` is 0, Swift may opt not to allocate any storage, and we'll
114-
// crash dereferencing the base address.
115-
let count = Swift.max(1, count)
116-
117-
// Create a copy of this collection. Note we don't automatically add a null
118-
// character at the end (for C strings) because that could mask bugs in test
119-
// code that should automatically be adding them.
120-
let resultPointer = UnsafeMutableBufferPointer<Element>.allocate(capacity: count)
121-
let initializedEnd = resultPointer.initialize(fromContentsOf: self)
122-
123-
expectationContext.callWhenDeinitializing {
124-
resultPointer[..<initializedEnd].deinitialize()
125-
resultPointer.deallocate()
126-
}
127-
128-
return resultPointer.baseAddress!
94+
@_disfavoredOverload
95+
@inlinable public mutating func callAsFunction(_ value: String?, _ id: __ExpressionID) -> String? {
96+
captureValue(value, id)
12997
}
130-
}
13198

132-
extension String: __ExpectationContext.__ImplicitlyPointerConvertible {
133-
@inlinable public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> UnsafeMutablePointer<CChar> {
134-
utf8CString.__implicitlyCast(for: &expectationContext)
99+
/// Capture an array for use if the expectation currently being evaluated
100+
/// fails.
101+
///
102+
/// - Parameters:
103+
/// - value: The value to pass through.
104+
/// - id: A value that uniquely identifies the represented expression in the
105+
/// context of the expectation currently being evaluated.
106+
///
107+
/// - Returns: `value`, verbatim.
108+
///
109+
/// This overload of `callAsFunction()` is used when an array is passed to
110+
/// allow for the correct handling of implicit C array conversion after it
111+
/// returns. For more information about implicit type conversions performed by
112+
/// the Swift compiler, see [here](https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter).
113+
///
114+
/// - Warning: This function is used to implement the `#expect()` and
115+
/// `#require()` macros. Do not call it directly.
116+
@inlinable public mutating func callAsFunction<E>(_ value: Array<E>, _ id: __ExpressionID) -> Array<E> {
117+
captureValue(value, id)
135118
}
136-
}
137119

138-
extension Optional: __ExpectationContext.__ImplicitlyPointerConvertible where Wrapped: __ExpectationContext.__ImplicitlyPointerConvertible {
139-
public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> Wrapped.__ImplicitPointerConversionResult? {
140-
flatMap { $0.__implicitlyCast(for: &expectationContext) }
120+
/// Capture an optional array for use if the expectation currently being
121+
/// evaluated fails.
122+
///
123+
/// - Parameters:
124+
/// - value: The value to pass through.
125+
/// - id: A value that uniquely identifies the represented expression in the
126+
/// context of the expectation currently being evaluated.
127+
///
128+
/// - Returns: `value`, verbatim.
129+
///
130+
/// This overload of `callAsFunction()` is used when an optional array is
131+
/// passed to allow for the correct handling of implicit C array conversion
132+
/// after it returns. For more information about implicit type conversions
133+
/// performed by the Swift compiler, see [here](https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter).
134+
///
135+
/// - Warning: This function is used to implement the `#expect()` and
136+
/// `#require()` macros. Do not call it directly.
137+
@_disfavoredOverload
138+
@inlinable public mutating func callAsFunction<E>(_ value: Array<E>?, _ id: __ExpressionID) -> Array<E>? {
139+
captureValue(value, id)
141140
}
142141
}
143-
144-
extension Array: __ExpectationContext.__ImplicitlyPointerConvertible {}
145-
extension ContiguousArray: __ExpectationContext.__ImplicitlyPointerConvertible {}
146142
#endif

Diff for: Sources/Testing/Expectations/ExpectationContext.swift

+1-21
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,6 @@ public struct __ExpectationContext: ~Copyable {
4343
/// are evaluated, much like ``runtimeValues``.
4444
var differences: [__ExpressionID: () -> CollectionDifference<Any>?]
4545

46-
/// Cleanup functions for any locally-created resources (such as pointers or
47-
/// C strings.)
48-
///
49-
/// The closures in this array are called when this instance is deinitialized.
50-
/// The effect of calling them elsewhere is undefined.
51-
private var _cleanup = [() -> Void]()
52-
53-
/// Register a callback to invoke when this instance is deinitialized.
54-
///
55-
/// - Parameters:
56-
/// - cleanup: The callback to invoke when `deinit` is called.
57-
mutating func callWhenDeinitializing(_ cleanup: @escaping () -> Void) {
58-
_cleanup.append(cleanup)
59-
}
60-
6146
init(
6247
sourceCode: @escaping @autoclosure @Sendable () -> [__ExpressionID: String] = [:],
6348
runtimeValues: [__ExpressionID: () -> Expression.Value?] = [:],
@@ -68,12 +53,6 @@ public struct __ExpectationContext: ~Copyable {
6853
self.differences = differences
6954
}
7055

71-
deinit {
72-
for cleanup in _cleanup {
73-
cleanup()
74-
}
75-
}
76-
7756
/// Collapse the given expression graph into one or more expressions with
7857
/// nested subexpressions.
7958
///
@@ -205,6 +184,7 @@ extension __ExpectationContext {
205184
///
206185
/// - Warning: This function is used to implement the `#expect()` and
207186
/// `#require()` macros. Do not call it directly.
187+
@_disfavoredOverload
208188
@inlinable public mutating func callAsFunction<T>(_ value: T, _ id: __ExpressionID) -> T {
209189
captureValue(value, id)
210190
}

0 commit comments

Comments
 (0)