Description
Description
Initializing an argument
that contains 3 or more cyclical classes causes an infinite recursion within __Expression.Value
's private init:
private init(
_reflecting subject: Any,
label: String?,
seenObjects: inout [ObjectIdentifier: AnyObject]
)
I tracked it down to the attempt to prevent cyclical dependencies. Using the sample code provided in Steps to Reproduce, I was able to maybe fix this by removing the defer
and adding a filter
onto the children:
private init(
_reflecting subject: Any,
label: String?,
seenObjects: inout [ObjectIdentifier: AnyObject]
) {
...
if shouldIncludeChildren, !mirror.children.isEmpty || isCollection {
children = mirror.children.**filter { child in
!seenObjects.contains(ObjectIdentifier(child as AnyObject))
}**.map { child in
Self(_reflecting: child.value, label: child.label, seenObjects: &seenObjects)
}
}
}
But I'm unsure if this has any unintended side effects (specifically, the comment callout about multiple references to the same object in the same subject). Logging a Value(reflecting: a)
in the makeA()
method returns me a Value
instance that looks to properly stop traversing future references of the same type after it traverses first one.
Expected behavior
Cyclical graphs of any complexity should resolve their reflection correctly
Actual behavior
Once 3 classes are introduced into the graph, the initializer enters an infinite loop
Steps to reproduce
- Attempt to run the following Test:
class A {
private var c: C!
private var c2: C!
private var b: B!
func setup(b: B, c: C) {
self.b = b
self.c = c
c2 = c
}
}
class B {
private var a: A!
private var c: C!
func setup(a: A, c: C) {
self.a = a
self.c = c
}
}
class C {
private var a: A!
private var b: B!
func setup(a: A, b: B) {
self.a = a
self.b = b
}
}
@Suite
struct TestingAppTests {
@Test(arguments: [makeA()]) func example(foo _: A) async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
#expect(true)
}
static func makeA() -> A {
let a = A()
let b = B()
let c = C()
a.setup(b: b, c: c)
b.setup(a: a, c: c)
c.setup(a: a, b: b)
return a
}
}
- Observe the process crashes after the return of
makeA()
and before the main test begins
swift-testing version/commit hash
Xcode's version of swift-testing
Swift & OS version (output of swift --version ; uname -a
)
swift-driver version: 1.115 Apple Swift version 6.0.2 (swiftlang-6.0.2.1.2 clang-1600.0.26.4)
Target: arm64-apple-macosx15.0
Darwin .local 24.2.0 Darwin Kernel Version 24.2.0: Tue Oct 15 18:15:36 PDT 2024; root:xnu-11215.60.364.501.5~3/RELEASE_ARM64_T6000 arm64