...which is confusing, because I expected that, in a ContiguousArray<Int>, if element 0 is located at address p, then element 1 would be located at address p + 8 (because MemoryLayout.stride == 8), because (i thought) that's what "contiguous" means: that there is no gap between any two adjacent elements.
What am I missing?
version info: macOS Ventura 13.6 (22G120)
% lldb --version
lldb-1500.0.22.8
Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Indeed. 0x0000600000521610 is a very odd-looking pointer for arm64.
The elements themselves are stored in the tail-allocated storage of a ContiguousArrayStorage object. I couldn’t find the code in the lldb repo that constructs the in-memory representation of Swift collections like ContiguousArray, but I suspect that code is using bogus location values, perhaps out of necessity to make Swift’s collections fit lldb’s classically-C++ understanding of arrays.
...leaves me feeling stumped about how to proceed wrt inspection of the Swift.__SwiftNativeNSArray subobject.
Presumably, it contains a pointer. If the program being paused/inspected were a C program, i would:
(lldb) expr -- *(T*)a.ptr
(Though, in C, there wouldn't be a base subobject.)
But i'm not sure how to get LLDB to print something useful about the Swift.__SwiftNativeNSArray base subobject of a._buffer._storage (and i used --raw-output, which you'd think would at least reveal the bit pattern of the pointer).
(note, i'm not trying to use the debugger to locate a bug; i'm just using it to try to build up an understanding of how ContiguousArray works.)
Sidestepping LLDB for a second, you can see the current pointer used by ContiguousArray by using withUnsafeBufferPointer:
expr a.withUnsafeBufferPointer { print($0) }
The documentation here still says
The pointer passed as an argument to body is valid only during the execution of withUnsafeBufferPointer(_:) . Do not store or return the pointer for later use.
so, don't do that, but in practice it's going to be the current storage for the array.
...which, using Builtin seems like a reasonable thing to do in a debugging context...
(but only in a debugging context, since you probably don't want any long-lived, non-stdlib src depending on Builtins)
i mean, we can already touch registers in a debugging context, which is not something you're normally able to do in a swift src file.
(though, for the specific example above, i guess there's less motivation to allow Builtins since we can use withUnsafeBufferPointer in the debugger (as @jrose suggested), which returns the result of Builtin.projectTailElems())
The Builtin module is only visible when building the standard library, which allows the stdlib implementation to be revlocked to the semantics of the compiler it is built with.
I don’t even think it’s a real module; I believe the compiler replaces references to Builtin symbols with literal SIL. Thus, if Builtin were available in the debugger, it would be from the version of Swift that the debugger was built with, not the one the host system’s standard library was built with. That could be problematic.