Skip to content

Commit 35aa479

Browse files
committed
Move the string- and pointer-handling code to its own file
1 parent 8a90515 commit 35aa479

File tree

3 files changed

+162
-148
lines changed

3 files changed

+162
-148
lines changed

Sources/Testing/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_library(Testing
4040
Expectations/Expectation+Macro.swift
4141
Expectations/ExpectationChecking+Macro.swift
4242
Expectations/ExpectationContext.swift
43+
Expectations/ExpectationContext+Pointers.swift
4344
Issues/Confirmation.swift
4445
Issues/ErrorSnapshot.swift
4546
Issues/Issue.swift
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
#if !SWT_NO_IMPLICIT_POINTER_CASTING
12+
// MARK: String-to-C-string handling and implicit pointer conversions
13+
14+
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
44+
}
45+
46+
/// Capture information about a value for use if the expectation currently
47+
/// being evaluated fails.
48+
///
49+
/// - Parameters:
50+
/// - value: The value to pass through.
51+
/// - id: A value that uniquely identifies the represented expression in the
52+
/// context of the expectation currently being evaluated.
53+
///
54+
/// - Returns: `value`, cast to a C string.
55+
///
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.
59+
///
60+
/// - Warning: This function is used to implement the `#expect()` and
61+
/// `#require()` macros. Do not call it directly.
62+
@_disfavoredOverload
63+
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S.__ImplicitPointerConversionResult where S: __ImplicitlyPointerConvertible {
64+
captureValue(value, id).__implicitlyCast(for: &self)
65+
}
66+
67+
/// Capture information about a value for use if the expectation currently
68+
/// being evaluated fails.
69+
///
70+
/// - Parameters:
71+
/// - value: The value to pass through.
72+
/// - id: A value that uniquely identifies the represented expression in the
73+
/// context of the expectation currently being evaluated.
74+
///
75+
/// - Returns: `value`, verbatim.
76+
///
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.
82+
///
83+
/// - Warning: This function is used to implement the `#expect()` and
84+
/// `#require()` macros. Do not call it directly.
85+
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S where S: __ImplicitlyPointerConvertible {
86+
captureValue(value, id)
87+
}
88+
89+
/// Convert some pointer to another pointer type and capture information about
90+
/// it for use if the expectation currently being evaluated fails.
91+
///
92+
/// - Parameters:
93+
/// - value: The pointer to cast.
94+
/// - id: A value that uniquely identifies the represented expression in the
95+
/// context of the expectation currently being evaluated.
96+
///
97+
/// - Returns: `value`, cast to another type of pointer.
98+
///
99+
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
100+
/// between various pointer types that are normally provided by the compiler.
101+
///
102+
/// - Warning: This function is used to implement the `#expect()` and
103+
/// `#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!
129+
}
130+
}
131+
132+
extension String: __ExpectationContext.__ImplicitlyPointerConvertible {
133+
@inlinable public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> UnsafeMutablePointer<CChar> {
134+
utf8CString.__implicitlyCast(for: &expectationContext)
135+
}
136+
}
137+
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) }
141+
}
142+
}
143+
144+
extension Array: __ExpectationContext.__ImplicitlyPointerConvertible {}
145+
extension ContiguousArray: __ExpectationContext.__ImplicitlyPointerConvertible {}
146+
#endif

Sources/Testing/Expectations/ExpectationContext.swift

Lines changed: 15 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
private import _TestingInternals
12-
1311
/// A type representing the context within a call to the `#expect()` and
1412
/// `#require()` macros.
1513
///
@@ -45,12 +43,20 @@ public struct __ExpectationContext: ~Copyable {
4543
/// are evaluated, much like ``runtimeValues``.
4644
var differences: [__ExpressionID: () -> CollectionDifference<Any>?]
4745

48-
#if !SWT_NO_IMPLICIT_POINTER_CASTING
49-
/// Storage for any locally-created C strings and pointers.
46+
/// Cleanup functions for any locally-created resources (such as pointers or
47+
/// C strings.)
5048
///
51-
/// For more information, see `__ImplicitlyPointerConvertible` below.
52-
fileprivate var temporaryPointerCleanup = [() -> ()]()
53-
#endif
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+
}
5460

5561
init(
5662
sourceCode: @escaping @autoclosure @Sendable () -> [__ExpressionID: String] = [:],
@@ -63,11 +69,9 @@ public struct __ExpectationContext: ~Copyable {
6369
}
6470

6571
deinit {
66-
#if !SWT_NO_IMPLICIT_POINTER_CASTING
67-
for temporaryPointerCleanup in temporaryPointerCleanup {
68-
temporaryPointerCleanup()
72+
for cleanup in _cleanup {
73+
cleanup()
6974
}
70-
#endif
7175
}
7276

7377
/// Collapse the given expression graph into one or more expressions with
@@ -448,140 +452,3 @@ extension __ExpectationContext {
448452
return result
449453
}
450454
}
451-
452-
#if !SWT_NO_IMPLICIT_POINTER_CASTING
453-
// MARK: - String-to-C-string handling and implicit pointer conversions
454-
455-
extension __ExpectationContext {
456-
/// A protocol describing types that can be implicitly cast to C strings or
457-
/// pointers when passed to C functions.
458-
///
459-
/// This protocol helps the compiler disambiguate string values when they need
460-
/// to be implicitly cast to C strings or other pointer types.
461-
///
462-
/// - Warning: This protocol is used to implement the `#expect()` and
463-
/// `#require()` macros. Do not use it directly. Do not add conformances to
464-
/// this protocol outside of the testing library.
465-
public protocol __ImplicitlyPointerConvertible {
466-
/// The concrete type of the resulting pointer when an instance of this type
467-
/// is implicitly cast.
468-
associatedtype __ImplicitPointerConversionResult
469-
470-
/// Perform an implicit cast of this instance to its corresponding pointer
471-
/// type.
472-
///
473-
/// - Parameters:
474-
/// - expectationContext: The expectation context that needs to cast this
475-
/// instance.
476-
///
477-
/// - Returns: A copy of this instance, cast to a pointer.
478-
///
479-
/// The implementation of this method should register the resulting pointer
480-
/// with `expectationContext` so that it is not leaked.
481-
///
482-
/// - Warning: This function is used to implement the `#expect()` and
483-
/// `#require()` macros. Do not call it directly.
484-
func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> __ImplicitPointerConversionResult
485-
}
486-
487-
/// Capture information about a value for use if the expectation currently
488-
/// being evaluated fails.
489-
///
490-
/// - Parameters:
491-
/// - value: The value to pass through.
492-
/// - id: A value that uniquely identifies the represented expression in the
493-
/// context of the expectation currently being evaluated.
494-
///
495-
/// - Returns: `value`, cast to a C string.
496-
///
497-
/// This overload of `callAsFunction(_:_:)` helps the compiler disambiguate
498-
/// string values when they need to be implicitly cast to C strings or other
499-
/// pointer types.
500-
///
501-
/// - Warning: This function is used to implement the `#expect()` and
502-
/// `#require()` macros. Do not call it directly.
503-
@_disfavoredOverload
504-
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S.__ImplicitPointerConversionResult where S: __ImplicitlyPointerConvertible {
505-
captureValue(value, id).__implicitlyCast(for: &self)
506-
}
507-
508-
/// Capture information about a value for use if the expectation currently
509-
/// being evaluated fails.
510-
///
511-
/// - Parameters:
512-
/// - value: The value to pass through.
513-
/// - id: A value that uniquely identifies the represented expression in the
514-
/// context of the expectation currently being evaluated.
515-
///
516-
/// - Returns: `value`, verbatim.
517-
///
518-
/// This overload of `callAsFunction(_:_:)` helps the compiler disambiguate
519-
/// string values when they do _not_ need to be implicitly cast to C strings
520-
/// or other pointer types. Without this overload, all instances of conforming
521-
/// types end up being cast to pointers before being compared (etc.), which
522-
/// produces incorrect results.
523-
///
524-
/// - Warning: This function is used to implement the `#expect()` and
525-
/// `#require()` macros. Do not call it directly.
526-
@inlinable public mutating func callAsFunction<S>(_ value: S, _ id: __ExpressionID) -> S where S: __ImplicitlyPointerConvertible {
527-
captureValue(value, id)
528-
}
529-
530-
/// Convert some pointer to another pointer type and capture information about
531-
/// it for use if the expectation currently being evaluated fails.
532-
///
533-
/// - Parameters:
534-
/// - value: The pointer to cast.
535-
/// - id: A value that uniquely identifies the represented expression in the
536-
/// context of the expectation currently being evaluated.
537-
///
538-
/// - Returns: `value`, cast to another type of pointer.
539-
///
540-
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
541-
/// between various pointer types that are normally provided by the compiler.
542-
///
543-
/// - Warning: This function is used to implement the `#expect()` and
544-
/// `#require()` macros. Do not call it directly.
545-
@inlinable public mutating func callAsFunction<P1, P2>(_ value: P1?, _ id: __ExpressionID) -> P2! where P1: _Pointer, P2: _Pointer {
546-
captureValue(value, id).flatMap { value in
547-
P2(bitPattern: Int(bitPattern: value))
548-
}
549-
}
550-
}
551-
552-
extension __ExpectationContext.__ImplicitlyPointerConvertible where Self: Collection {
553-
public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> UnsafeMutablePointer<Element> {
554-
// If `count` is 0, Swift may opt not to allocate any storage, and we'll
555-
// crash dereferencing the base address.
556-
let count = Swift.max(1, count)
557-
558-
// Create a copy of this collection. Note we don't automatically add a null
559-
// character at the end (for C strings) because that could mask bugs in test
560-
// code that should automatically be adding them.
561-
let resultPointer = UnsafeMutableBufferPointer<Element>.allocate(capacity: count)
562-
let initializedEnd = resultPointer.initialize(fromContentsOf: self)
563-
564-
expectationContext.temporaryPointerCleanup.append {
565-
resultPointer[..<initializedEnd].deinitialize()
566-
resultPointer.deallocate()
567-
}
568-
569-
return resultPointer.baseAddress!
570-
}
571-
}
572-
573-
extension String: __ExpectationContext.__ImplicitlyPointerConvertible {
574-
@inlinable public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> UnsafeMutablePointer<CChar> {
575-
utf8CString.__implicitlyCast(for: &expectationContext)
576-
}
577-
}
578-
579-
extension Optional: __ExpectationContext.__ImplicitlyPointerConvertible where Wrapped: __ExpectationContext.__ImplicitlyPointerConvertible {
580-
public func __implicitlyCast(for expectationContext: inout __ExpectationContext) -> Wrapped.__ImplicitPointerConversionResult? {
581-
flatMap { $0.__implicitlyCast(for: &expectationContext) }
582-
}
583-
}
584-
585-
extension Array: __ExpectationContext.__ImplicitlyPointerConvertible {}
586-
extension ContiguousArray: __ExpectationContext.__ImplicitlyPointerConvertible {}
587-
#endif

0 commit comments

Comments
 (0)