I recently added a noncopyable type to our codebase, and then another developer added a piece of code that I found very surprising:
struct MyThing: ~Copyable {}
protocol P {
var thing: MyThing { get }
}
This compiles. Why? How? What does it mean? My colleague was then able to witness this requirement with a stored property, and with a computed property with a _read accessor (which may or may not be about to be publicized as yielding borrow depending on the outcome of @Joe_Groff's SE-0474). Does that mean the { get } requirement has been implicitly translated to a { yielding borrow } requirement because the type was noncopyable? Is this intended to remain as a long-term... feature? of the language?
the linked proposal contains the following regarding this question:
Property and subscript requirements in protocols have up to this point only been able to express readability in terms of get requirements. This becomes insufficient when the requirement has a noncopyable type:
protocol Producing {
var property: UniqueString { get }
}
To fulfill such a requirement, the conformance must provide a getter, meaning its implementation must be able to produce a value whose ownership can be transferred to the caller. In practical terms, this means that the requirement cannot be witnessed by a stored property or a yielding borrow accessor when the result is of noncopyable type, since the storage of a stored property is owned by the containing aggregate and the result of a yielding borrow is owned by the suspended coroutine, and it would be necessary to copy to provide ownership to the caller. However, if the type of the get requirement is copyable, the compiler can synthesize the getter from the other accessor kinds by introducing copies as necessary.
The current behavior is a bug. yielding borrow is used as the ABI entry point for noncopyable members because that is the most conservative accessor that can be implemented—yielding borrow can be derived from get without copying, but not the other way around—but when protocols or classes explicitly express their interface in terms of get we ought to honor that.
So… to be clear, we expect that if/when SE-0474 lands, it will be a compiler error to have { get } on a property of noncopyable type in a protocol, and { yielding borrow } will be accepted in that position instead?
That's right, since a stored property's value cannot be consumed without also consuming the rest of the containing value. You'd want to use { yielding borrow } for a requirement that can be satisfied by either stored or computed properties.