<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Oct 13, 2016, at 10:36 AM, Joe Groff <<a href="mailto:jgroff@apple.com" class="">jgroff@apple.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class=""><br class=""><blockquote type="cite" class="">On Oct 11, 2016, at 4:48 PM, Erik Eckstein via swift-dev <<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>> wrote:<br class=""><br class="">This is a proposal for representing copy-on-write buffers in SIL. Actually it’s still a draft for a proposal. It also heavily depends on how we move forward with SIL ownership.<br class=""><CopyOnWrite.rst><br class="">If you have any comments, please let me know.<br class=""></blockquote><br class="">The SIL-level design seems sensible to me at a glance. At the language level, I think it would make more sense to treat this as an attribute on class types rather than on properties in structs using the class. I don't think many people reuse class definitions as both shared reference types and as value type payloads, but beyond that, I think that making it an attribute of classes would put us into a better position to leverage the borrow model to enforce the "mutable-only-when-unique" aspect of COW implementations. </div></div></blockquote><div><br class=""></div><div>I think this makes sense. Although we would need an attribute on the field as well (what John called the @copied attribute).</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">John alluded to this in the "SIL address types and borrowing" thread:<br class=""><br class=""></div></div></blockquote><blockquote type="cite" class=""><div class=""><div class=""><blockquote type="cite" class="">I wonder if it would make more sense to make copy-on-write buffer references a move-only type, so that as long as you were just working with the raw reference (as opposed to the CoW aggregate, which would remain copyable) it wouldn't get implicitly copied anymore. You could have mutable and immutable buffer reference types, both move-only, and there could be a consuming checkUnique operation on the immutable one that, I dunno, returned an Either of the mutable and immutable versions.<br class=""><br class="">For CoW aggregates, you'd need some @copied attribute on the field to make sure that the CoW attribute was still copyable. Within the implementation of the type, though, you would be projecting out the reference immediately, and thereafter you'd be certain that you were borrowing / moving it around as appropriate.<br class=""></blockquote><br class="">If 'copy-on-write' were a trait on classes, then we could distinguish unique and nonunique references to the class. A unique reference would act like a move-only type to prevent accidental loss of uniqueness. We can also allow a copy-on-write class to have "mutating" methods, and only allow mutation on unique references.</div></div></blockquote><div><br class=""></div><div>The only way to get a unique reference would then be to use the isUnique-builtin - or to create a new buffer. This is good, because we could statically check that the programmer can only modify uniquely referenced buffers (although to get full memory safety we would probably have to invalidate the original buffer reference between the is_unique - end_unique scope, e.g. by storing a null pointer into it).</div><div><br class=""></div><div>Thinking about this in detail I believe we have to change how a COW is implemented. For example we could not write back the unique reference and use it afterwards:</div><div><br class=""></div><div><font face="Menlo" class="">if let uniqueRef = isUnique(&cow.buffer) {</font></div><div><span style="font-family: Menlo;" class="">} else {</span></div><div><font face="Menlo" class=""> uniqueRef = Buffer()</font></div><div><font face="Menlo" class=""> cow.buffer = uniqueRef // <- this would not work. It’s a copy of a unique buffer</font></div><div><font face="Menlo" class="">}</font></div><div><font face="Menlo" class="">uniqueRef.someData = ...</font></div><div><br class=""></div><div>Instead this could be done like</div><div><br class=""></div><div><div><font face="Menlo" class="">if let uniqueRef = isUnique(&cow.buffer) {</font></div><div><span style="font-family: Menlo;" class="">} else {</span></div><div><font face="Menlo" class=""> uniqueRef = Buffer()</font></div><div><font face="Menlo" class="">}</font></div><div><font face="Menlo" class="">uniqueRef.someData = ...</font></div><div class=""><div><font face="Menlo" class="">cow.buffer = uniqueRef // OK, end of lifetime of uniqueRef</font></div></div><div class=""><br class=""></div><div class="">It would mean that we write back the buffer even in the unique-case, But I think this is ok.</div><div class=""><br class=""></div></div><div>For the implementation we would need two different reference types in the AST (unique and nonunique), right? Could this be just a different StorageType?</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">It seems to me like, exploring this direction, we could also come up with a way for the high-level value-semantics operations on the struct to statically indicate which methods are known to leave the value's buffers in a unique state, or which return values that are uniquely owned, which would give the optimizer more ability to avoid uniqueness checks across calls without relying on inlining and IPO.<br class=""><br class="">-Joe</div></div></blockquote></div><br class=""></body></html>