Skip to content

Commit e6eadc8

Browse files
committed
Try to clean up pointers
1 parent 141f629 commit e6eadc8

File tree

6 files changed

+75
-99
lines changed

6 files changed

+75
-99
lines changed

Sources/Testing/Expectations/ExpectationContext.swift

+35-83
Original file line numberDiff line numberDiff line change
@@ -463,72 +463,9 @@ extension __ExpectationContext {
463463
///
464464
/// - Warning: This function is used to implement the `#expect()` and
465465
/// `#require()` macros. Do not call it directly.
466-
@_disfavoredOverload
467-
public mutating func callAsFunction<P, T>(_ value: P, _ id: __ExpressionID) -> UnsafePointer<T> where P: _Pointer, P.Pointee == T {
468-
self(value as P?, id)!
469-
}
470-
471-
/// Convert some pointer to an immutable one and capture information about it
472-
/// for use if the expectation currently being evaluated fails.
473-
///
474-
/// - Parameters:
475-
/// - value: The pointer to make immutable.
476-
/// - id: A value that uniquely identifies the represented expression in the
477-
/// context of the expectation currently being evaluated.
478-
///
479-
/// - Returns: `value`, cast to an immutable pointer.
480-
///
481-
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
482-
/// between various pointer types that are normally provided by the compiler.
483-
///
484-
/// - Warning: This function is used to implement the `#expect()` and
485-
/// `#require()` macros. Do not call it directly.
486-
@_disfavoredOverload
487-
public mutating func callAsFunction<P, T>(_ value: P?, _ id: __ExpressionID) -> UnsafePointer<T>? where P: _Pointer, P.Pointee == T {
488-
value.flatMap { value in
489-
UnsafePointer<T>(bitPattern: Int(bitPattern: self(value, id) as P))
490-
}
491-
}
492-
493-
/// Convert some pointer to an immutable one and capture information about it
494-
/// for use if the expectation currently being evaluated fails.
495-
///
496-
/// - Parameters:
497-
/// - value: The pointer to make immutable.
498-
/// - id: A value that uniquely identifies the represented expression in the
499-
/// context of the expectation currently being evaluated.
500-
///
501-
/// - Returns: `value`, cast to an immutable pointer.
502-
///
503-
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
504-
/// between various pointer types that are normally provided by the compiler.
505-
///
506-
/// - Warning: This function is used to implement the `#expect()` and
507-
/// `#require()` macros. Do not call it directly.
508-
@_disfavoredOverload
509-
public mutating func callAsFunction<P>(_ value: P, _ id: __ExpressionID) -> UnsafeRawPointer where P: _Pointer {
510-
self(value as P?, id)!
511-
}
512-
513-
/// Convert some pointer to an immutable one and capture information about it
514-
/// for use if the expectation currently being evaluated fails.
515-
///
516-
/// - Parameters:
517-
/// - value: The pointer to make immutable.
518-
/// - id: A value that uniquely identifies the represented expression in the
519-
/// context of the expectation currently being evaluated.
520-
///
521-
/// - Returns: `value`, cast to an immutable pointer.
522-
///
523-
/// This overload of `callAsFunction(_:_:)` handles the implicit conversions
524-
/// between various pointer types that are normally provided by the compiler.
525-
///
526-
/// - Warning: This function is used to implement the `#expect()` and
527-
/// `#require()` macros. Do not call it directly.
528-
@_disfavoredOverload
529-
public mutating func callAsFunction<P>(_ value: P?, _ id: __ExpressionID) -> UnsafeRawPointer? where P: _Pointer {
530-
value.flatMap { value in
531-
UnsafeRawPointer(bitPattern: Int(bitPattern: self(value, id) as P))
466+
public mutating func callAsFunction<P1, P2>(_ value: P1?, _ id: __ExpressionID) -> P2! where P1: _Pointer, P2: _Pointer {
467+
self(value as P1?, id).flatMap { value in
468+
P2(bitPattern: Int(bitPattern: value))
532469
}
533470
}
534471
}
@@ -550,35 +487,50 @@ extension __ExpectationContext {
550487
/// context is destroyed.
551488
///
552489
/// This overload of `callAsFunction(_:_:)` is necessary because Swift allows
553-
/// passing string literals directly to functions that take C strings. At
554-
/// compile time, the compiler generates code that makes a temporary UTF-8
555-
/// copy of the string, then frees that copy on return. That logic does not
556-
/// work correctly when strings are passed to intermediate functions such as
557-
/// this one, and the compiler will fail to extend the lifetime of the C
558-
/// strings to the appropriate point. ([122011759](rdar://122011759))
490+
/// passing string literals directly to functions that take C strings. The
491+
/// default overload of `callAsFunction(_:_:)` does not trigger this implicit
492+
/// cast and causes a compile-time error. ([122011759](rdar://122011759))
559493
///
560494
/// - Warning: This function is used to implement the `#expect()` and
561495
/// `#require()` macros. Do not call it directly.
562-
public mutating func callAsFunction<P>(_ value: String, _ id: __ExpressionID) -> P where P: _Pointer, P.Pointee == CChar {
496+
public mutating func callAsFunction<P>(_ value: String, _ id: __ExpressionID) -> P where P: _Pointer {
563497
// Perform the normal value capture.
564-
let result = self(value, id) as String
498+
let value = self(value as String, id)
565499

566500
// Create a C string copy of `value`.
501+
let valueCString = value.withCString { value in
567502
#if os(Windows)
568-
let resultCString = _strdup(result)!
503+
_strdup(value)
569504
#else
570-
let resultCString = strdup(result)!
505+
strdup(value)
571506
#endif
507+
}
508+
509+
let result = valueCString.flatMap { valueCString in
510+
// Store the C string pointer so we can free it later when this context is
511+
// torn down.
512+
if _transformedCStrings.capacity == 0 {
513+
_transformedCStrings.reserveCapacity(2)
514+
}
515+
_transformedCStrings.append(valueCString)
572516

573-
// Store the C string pointer so we can free it later when this context is
574-
// torn down.
575-
if _transformedCStrings.capacity == 0 {
576-
_transformedCStrings.reserveCapacity(2)
517+
// Return the C string as whatever pointer type the caller wants.
518+
return P(bitPattern: Int(bitPattern: valueCString))
577519
}
578-
_transformedCStrings.append(resultCString)
579520

580-
// Return the C string as whatever pointer type the caller wants.
581-
return P(bitPattern: Int(bitPattern: resultCString)).unsafelyUnwrapped
521+
return result!
522+
}
523+
524+
/// Capture information about a value for use if the expectation currently
525+
/// being evaluated fails.
526+
///
527+
/// This overload of `callAsFunction(_:_:)` helps the compiler disambiguate
528+
/// optional string values.
529+
///
530+
/// - Warning: This function is used to implement the `#expect()` and
531+
/// `#require()` macros. Do not call it directly.
532+
public mutating func callAsFunction(_ value: String?, _ id: __ExpressionID) -> String! {
533+
self(value as String?, id)
582534
}
583535
}
584536
#endif

Sources/TestingMacros/Support/ConditionArgumentParsing.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,12 @@ func findEffectKeywords(in node: some SyntaxProtocol) -> Set<Keyword> {
735735
///
736736
/// - Returns: A copy of `token` as an identifier token.
737737
private func _rewriteDollarIdentifier(_ token: TokenSyntax) -> TokenSyntax {
738-
.identifier("__renamedCapture__\(token.trimmedDescription)")
738+
var result = TokenSyntax.identifier("__renamedCapture__\(token.trimmedDescription)")
739+
740+
result.leadingTrivia = token.leadingTrivia
741+
result.trailingTrivia = token.trailingTrivia
742+
743+
return result
739744
}
740745

741746
/// A syntax rewriter that replaces _numeric_ dollar identifiers (e.g. `$0`)

Sources/_TestingInternals/include/TestSupport.h

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ static inline bool swt_pointersNotEqual4(const char *a, const char *b, const cha
3737
return a != b && b != c && c != d;
3838
}
3939

40+
static inline bool swt_nullableCString(const char *_Nullable string) {
41+
return string != 0;
42+
}
43+
4044
SWT_ASSUME_NONNULL_END
4145

4246
#endif

Tests/SubexpressionShowcase/SubexpressionShowcase.swift

+1-6
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func subexpressionShowcase() async throws {
5050
}
5151

5252
let closure: (Int) -> Void = {
53-
#expect(($0 + $0 + $0) == 0x10)
53+
#expect((($0 + $0 + $0) as Int) == 0x10)
5454
}
5555
closure(11)
5656

@@ -114,9 +114,4 @@ func subexpressionShowcase() async throws {
114114

115115
let n = 1 as Any
116116
_ = try #require(n as? String)
117-
118-
let utf16 = [UTF16.CodeUnit](repeating: 0, count: 16)
119-
_ = try utf16.withUnsafeBufferPointer { utf16 in
120-
try #require(String.decodeCString(utf16.baseAddress, as: UTF16.self)?.result)
121-
}
122117
}

Tests/TestingTests/Support/CartesianProductTests.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ struct CartesianProductTests {
5959

6060
// NOTE: we need to break out the tuple elements because tuples aren't
6161
// directly equatable.
62-
#expect(Array(product).map(\.0) == possibleValues.map(\.0))
63-
#expect(Array(product).map(\.1) == possibleValues.map(\.1))
62+
let productArray = Array(product)
63+
#expect(productArray.map(\.0) == possibleValues.map(\.0))
64+
#expect(productArray.map(\.1) == possibleValues.map(\.1))
6465
}
6566

6667
@Test("Cartesian product with empty first input is empty")

Tests/TestingTests/VariadicGenericTests.swift

+26-7
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@ import Testing
1212
private import _TestingInternals
1313

1414
@Test func variadicCStringArguments() async throws {
15-
#expect(0 == strcmp("abc", "abc"))
16-
#expect(0 != strcmp("abc", "123"))
17-
#expect(swt_pointersNotEqual2("abc", "123"))
18-
#expect(swt_pointersNotEqual3("abc", "123", "def"))
19-
#expect(swt_pointersNotEqual4("abc", "123", "def", "456"))
15+
let abc = "abc"
16+
let _123 = "123"
17+
let def = "def"
18+
let _456 = "456"
19+
#expect(0 == strcmp(abc, abc))
20+
#expect(0 != strcmp(abc, _123))
21+
#expect(swt_pointersNotEqual2(abc, _123))
22+
#expect(swt_pointersNotEqual3(abc, _123, def))
23+
#expect(swt_pointersNotEqual4(abc, _123, def, _456))
2024

21-
let lhs = "abc"
22-
let rhs = "123"
25+
let nilString: String? = nil
26+
#expect(swt_nullableCString(nilString) == false)
27+
28+
let lhs: String? = "abc"
29+
let rhs: String? = "123"
2330
#expect(0 != strcmp(lhs, rhs))
2431
}
2532

@@ -33,3 +40,15 @@ private import _TestingInternals
3340
#expect(endptr?.pointee == 0)
3441
}
3542
}
43+
44+
@Test func utf16PointerConversions() throws {
45+
_ = try withUnsafeTemporaryAllocation(of: UTF16.CodeUnit.self, capacity: 1) { buffer in
46+
func f(_ p: UnsafeRawPointer?) -> Bool { true }
47+
func g(_ p: UnsafeMutableRawPointer?) -> Bool { true }
48+
func h(_ p: UnsafeMutablePointer<UTF16.CodeUnit>?) -> Bool { true }
49+
#expect(f(buffer.baseAddress))
50+
#expect(g(buffer.baseAddress))
51+
#expect(h(buffer.baseAddress))
52+
return try #require(String.decodeCString(buffer.baseAddress, as: UTF16.self)?.result)
53+
}
54+
}

0 commit comments

Comments
 (0)