@@ -39,22 +39,28 @@ public final class __ExpectationContext<Output> where Output: ~Copyable {
3939 /// will have runtime values: notably, if an operand to a short-circuiting
4040 /// binary operator like `&&` is not evaluated, the corresponding expression
4141 /// will not be assigned a runtime value.
42- var runtimeValues : [ __ExpressionID : ( ) -> Expression . Value ? ]
42+ ///
43+ /// This property is non-optional because the evaluation of an expectation
44+ /// always produces at least one element.
45+ var runtimeValues : [ ( __ExpressionID , ( ) -> Expression . Value ? ) ]
4346
4447 /// Computed differences between the operands or arguments of expressions.
4548 ///
4649 /// The values in this dictionary are gathered at runtime as subexpressions
4750 /// are evaluated, much like ``runtimeValues``.
48- var differences : [ __ExpressionID : ( ) -> CollectionDifference < Any > ? ]
51+ ///
52+ /// This value is optional because, in the common case, an expectation does
53+ /// not produce a difference.
54+ var differences : [ ( __ExpressionID , ( ) -> CollectionDifference < Any > ? ) ] ?
4955
5056 init (
51- sourceCode: @escaping @autoclosure @Sendable ( ) -> KeyValuePairs < __ExpressionID , String > = [ : ] ,
52- runtimeValues: [ __ExpressionID : ( ) -> Expression . Value ? ] = [ : ] ,
53- differences: [ __ExpressionID : ( ) -> CollectionDifference < Any > ? ] = [ : ]
57+ sourceCode: @escaping @autoclosure @Sendable ( ) -> KeyValuePairs < __ExpressionID , String > ,
58+ runtimeValues: KeyValuePairs < __ExpressionID , ( ) -> Expression . Value ? > ? = nil ,
59+ differences: KeyValuePairs < __ExpressionID , ( ) -> CollectionDifference < Any > ? > ? = nil
5460 ) {
5561 _sourceCode = sourceCode
56- self . runtimeValues = runtimeValues
57- self . differences = differences
62+ self . runtimeValues = runtimeValues. map ( Array . init ) ?? [ ]
63+ self . differences = differences. map ( Array . init )
5864 }
5965
6066 /// Collapse the given expression graph into one or more expressions with
@@ -122,12 +128,14 @@ public final class __ExpectationContext<Output> where Output: ~Copyable {
122128 }
123129 }
124130
125- for (id, difference) in differences {
126- let keyPath = id. keyPathRepresentation
127- if var expression = expressionGraph [ keyPath] , let difference = difference ( ) {
128- let differenceDescription = Self . _description ( of: difference)
129- expression. differenceDescription = differenceDescription
130- expressionGraph [ keyPath] = expression
131+ if let differences {
132+ for (id, difference) in differences {
133+ let keyPath = id. keyPathRepresentation
134+ if var expression = expressionGraph [ keyPath] , let difference = difference ( ) {
135+ let differenceDescription = Self . _description ( of: difference)
136+ expression. differenceDescription = differenceDescription
137+ expressionGraph [ keyPath] = expression
138+ }
131139 }
132140 }
133141 }
@@ -159,6 +167,19 @@ extension __ExpectationContext: Sendable where Output: ~Copyable {}
159167// MARK: - Expression capturing
160168
161169extension __ExpectationContext where Output: ~ Copyable {
170+ /// Capture information about a value, encapsulated in an instance of
171+ /// ``Expression/Value``, for use if the expectation currently being evaluated
172+ /// fails.
173+ ///
174+ /// - Parameters:
175+ /// - runtimeValue: The value to pass through. This value is lazily
176+ /// evaluated on expectation failure only.
177+ /// - id: A value that uniquely identifies the represented expression in the
178+ /// context of the expectation currently being evaluated.
179+ func captureValue( _ runtimeValue: @escaping @autoclosure ( ) -> Expression . Value ? , identifiedBy id: __ExpressionID ) {
180+ runtimeValues. append ( ( id, runtimeValue) )
181+ }
182+
162183 /// Capture information about a value for use if the expectation currently
163184 /// being evaluated fails.
164185 ///
@@ -167,15 +188,13 @@ extension __ExpectationContext where Output: ~Copyable {
167188 /// - id: A value that uniquely identifies the represented expression in the
168189 /// context of the expectation currently being evaluated.
169190 ///
170- /// - Returns: `value`, verbatim.
171- ///
172191 /// This function helps subscript overloads disambiguate themselves and avoid
173192 /// accidental recursion.
174- func captureValue< T> ( _ value: borrowing T , _ id: __ExpressionID ) where T: ~ Copyable & ~ Escapable {
193+ func captureValue< T> ( _ value: borrowing T , identifiedBy id: __ExpressionID ) where T: ~ Copyable & ~ Escapable {
175194 if #available( _castingWithNonCopyableGenerics, * ) , let value = makeExistential ( value) {
176- runtimeValues [ id ] = { Expression . Value ( reflecting: value) }
195+ captureValue ( Expression . Value ( reflecting: value) , identifiedBy : id )
177196 } else {
178- runtimeValues [ id ] = { Expression . Value ( failingToReflectInstanceOf: T . self) }
197+ captureValue ( Expression . Value ( failingToReflectInstanceOf: T . self) , identifiedBy : id )
179198 }
180199 }
181200
@@ -193,7 +212,7 @@ extension __ExpectationContext where Output: ~Copyable {
193212 /// `#require()` macros. Do not call it directly.
194213 @_lifetime ( borrow value)
195214 public func callAsFunction< T> ( _ value: borrowing T , _ id: __ExpressionID ) -> T where T: ~ Escapable {
196- captureValue ( value, id)
215+ captureValue ( value, identifiedBy : id)
197216 return copy value
198217 }
199218
@@ -208,7 +227,7 @@ extension __ExpectationContext where Output: ~Copyable {
208227 /// - Warning: This function is used to implement the `#expect()` and
209228 /// `#require()` macros. Do not call it directly.
210229 public func __inoutAfter< T> ( _ value: inout T , _ id: __ExpressionID ) where T: ~ Copyable & ~ Escapable {
211- captureValue ( value, id)
230+ captureValue ( value, identifiedBy : id)
212231 }
213232}
214233
@@ -253,12 +272,13 @@ extension __ExpectationContext where Output: ~Copyable {
253272 /// compile-time pressure on the type checker that we don't want.
254273 func captureDifferences< T, U> ( _ lhs: T , _ rhs: U , _ opID: __ExpressionID ) {
255274#if !hasFeature(Embedded) // no existentials
275+ var difference : ( ( ) -> CollectionDifference < Any > ? ) ?
256276 if let lhs = lhs as? any StringProtocol {
257277 func open< V> ( _ lhs: V , _ rhs: U ) where V: StringProtocol {
258278 guard let rhs = rhs as? V else {
259279 return
260280 }
261- differences [ opID ] = {
281+ difference = {
262282 // Compare strings by line, not by character.
263283 let lhsLines = String ( lhs) . split ( whereSeparator: \. isNewline)
264284 let rhsLines = String ( rhs) . split ( whereSeparator: \. isNewline)
@@ -290,7 +310,7 @@ extension __ExpectationContext where Output: ~Copyable {
290310 let elementType = V . Element. self as? any Equatable . Type else {
291311 return
292312 }
293- differences [ opID ] = {
313+ difference = {
294314 func open< E> ( _: E . Type ) -> CollectionDifference < Any > where E: Equatable {
295315 let lhs : some BidirectionalCollection < E > = lhs. lazy. map { $0 as! E }
296316 let rhs : some BidirectionalCollection < E > = rhs. lazy. map { $0 as! E }
@@ -301,6 +321,14 @@ extension __ExpectationContext where Output: ~Copyable {
301321 }
302322 open ( lhs, rhs)
303323 }
324+
325+ if let difference {
326+ if differences == nil {
327+ differences = [ ( opID, difference) ]
328+ } else {
329+ differences? . append ( ( opID, difference) )
330+ }
331+ }
304332#endif
305333 }
306334
@@ -329,11 +357,11 @@ extension __ExpectationContext where Output: ~Copyable {
329357 _ rhs: borrowing U ,
330358 _ rhsID: __ExpressionID
331359 ) throws ( E) -> Bool where T: ~ Copyable, U: ~ Copyable {
332- captureValue ( lhs, lhsID)
333- captureValue ( rhs, rhsID)
360+ captureValue ( lhs, identifiedBy : lhsID)
361+ captureValue ( rhs, identifiedBy : rhsID)
334362
335363 let result = try op ( lhs, rhs)
336- captureValue ( result, opID)
364+ captureValue ( result, identifiedBy : opID)
337365
338366 if !result,
339367 #available( _castingWithNonCopyableGenerics, * ) ,
@@ -370,12 +398,12 @@ extension __ExpectationContext where Output: ~Copyable {
370398 public func __as< T, U> ( _ value: borrowing T , _ valueID: __ExpressionID , _ type: U . Type , _ typeID: __ExpressionID ) -> U ? {
371399 let value = copy value
372400
373- captureValue ( value, valueID)
401+ captureValue ( value, identifiedBy : valueID)
374402 let result = value as? U
375403
376404 if result == nil {
377405 let correctType = Swift . type ( of: value as Any )
378- captureValue ( correctType, typeID)
406+ captureValue ( correctType, identifiedBy : typeID)
379407 }
380408
381409 return result
0 commit comments