Motivation
Currently, passing a keypath to print()
, or to the po command
in LLDB, yields the standard output for a swift object. This is not very useful. For example, given
struct Theme {
var background: Color
var foreground: Color
}
print(\Theme.backgroundColor)
would have an output of roughly
Swift.KeyPath<Theme, Color>
which doesn't allow foregroundColor
to be distinguished from any other property on Theme
.
Ideally, the output would be
\Theme.backgroundColor
exactly as it was written in the program.
Swift Keypaths that represent variables visible in Objective-C can take advantage of the _kvcString
property to access something resembling a useful output. However, emitting a value for _kvcString
in all keypaths would create a code size increase that might not be acceptable for some Swift programs.
Design Goals
-
make it easier to debug programs that use keypaths by outputting a description that matches how the keypath would be written as a keypath literal.
-
potentially lay groundwork for more sophisticated reflection APIs to inspect keypaths, or, if there is significant interest in this, expand the scope to include those APIs
-
do so without significant changes to how keypaths are stored in memory or otherwise modifying code paths that are not related to debugging/introspecting key paths. I'm assuming that decisions like not filling out the equivalent of _kvcString for every keypath were taken for some design purpose (either code size or minimizing memory usage), but if that's not the case then this proposal might be implemented more effectively in other ways
Summary of the implementation
A sample implementation is available here.
Much like the _project
functions currently implemented in KeyPath.swift, this function would loop through the keypath's buffer, handling each segment as follows:
For offset segments, the implementation is simple: use _getRecursiveChildCount
, _getChildOffset
, and _getChildMetadata
to get the string name of the property. I believe these are the same mechanisms used by Mirror
today.
For optional chain, force-unwrap, etc. the function appends a hard coded "?"
or "!"
, as appropriate.
For computed segments, call dladdr
(or the appropriate equivalent on Windows) on the result of getter()
in the ComputedAccessorsPtr
. Demangle the result to get the property name.
Subscripts
Subscripts in Swift keypaths store their arguments in an opaque ArgumentRef
struct, which does not have any way to retrieve a description of the data it's storing. For that reason, the output of a subscript declared as subscript(string: String) -> String
would simply be \TypeName.subscript(_:)
.