Hello folks.
I've tried to understand how the recently introduced inlineArray implements subcripts, as its elements are ~NonCopyable by default, it is impossible (or at least very hard and I don't know how) to implement these using the standard get/set facilities builtin in the language. For curiosity, I took a look at the source code and I've found the following constructs
@_transparent
unsafeAddress {
// some code
}
///
@_transparent
unsafeMutableAddress {
// some code
}
They are also used to implement Spans, UnsafePointer and UnsafeBufferPointer.
The obvious question is:
They are used to provide a value of T at +0/guaranteed/borrow. For InlineArray<_, Atomic<Int>> as an example, it allows the subscript to "return" an Atomic<Int> and operate on it through the address. In the case of noncopyable, it means you cannot copy or move the value out of this subscript, which is quite common to do with regular copyable types:
let ptr1: UnsafePointer<Int> = ...
let ptr2: UnsafePointer<Atomic<Int>> = ...
// Ok, we are copying the value from the unsafeAddress
let x = ptr1.pointee
// error: 'ptr2.pointee' is borrowed and cannot be consumed
let y = ptr2.pointee
I assume that for ~Copyable that's the only way to implement getters and setters right now?
For types that are Copyable, if for some reason one wants to implement logic that operates "in memory" instead of copying, can these be used too?
Is it possible to have both get and set and unsafeAddress and unsafeMutableAddress for same property (of Copyable type? ). How does it work?
PS: Are these not documented at all? (Comments within the compiler code would be better than nothing). Are they supposed not to be used in code external to the language implementation?
Yeah, this feature is mostly an implementation detail for certain standard library types, it predates Swift evolution and is not memory safe in any way.
Well, that depends. It really depends on what you want your semantics to be for the "getter"/"setter" of a property/subscript. It's totally valid to have something like this:
var something: Noncopyable {
Noncopyable()
}
and this is just using a regular get for the noncopyable. This returns its value as +1/owned/consuming meaning the caller of the property now owns the noncopyable value. In most cases with collectiony types, the collection itself owns the noncopyable value and only wants users to be able to borrow or peek at the noncopyable value.
There is also _read/_modify (which has a proposal to make these public: SE-0474: Yielding Accessors) which allow you to write:
public struct UniqueString : ~Copyable {...}
public struct UniqueBorrow : ~Copyable {
var _x: UniqueString
public var property: UniqueString {
yielding borrow {
yield x
}
}
}
which will let users have a +0/guaranteed/borrow of the UniqueString that's currently being owned by UniqueBorrow.
Sort of. These accessors provide the illusion that all you're working with is in memory values, but most of the time you need to load values out of memory to be able to do simple things like call methods on it (depending on the ABI of the type). Consider the following example:
extension Int {
func hello() {
print("Hello! I am \(self)")
}
}
The ABI of the function hello directly takes self by value here because Int is not what's considered an "address only" type (and because self is just a simple integer that can be passed around in registers). So a type that has something like:
var property: Int {
unsafeAddress {
...
}
}
is providing the +0 borrow of the int, but doing something like property.hello() will immediately load the integer out of memory into a register to be able to perform the method call hello.
It's not possible to have both on the same property, you must have either a getter or addressor.