<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Here are my thoughts on implementing property behaviors. I think we can use an approach that instantiates storage at compile time, in order to avoid introducing type metadata, while still using separate compilation of implementations, by treating the behavior similarly to a protocol. We should be able to get optimization benefits from inlining using our existing and planned future optimization framework.<div class=""><br class=""></div><div class="">When a property is declared using a behavior, we emit a vtable referencing the interesting aspects of the property declaration:</div><div class=""><br class=""></div><div class="">- A projection function from Self to the behavior's storage for the property (which can be a pointer projection, since we know the behavior storage is stored),</div><div class="">- The accessors, lowered as methods on the containing Self type;</div><div class="">- If the initializer is bound, then a function that evaluates the initializer expression. If we have the eager/deferred distinction, then an eager initializer is () -> Value, and a deferred initializer is (Self) -> Value.</div><div class="">- If the property's name is bound, then a reference to the global string constant (which can be handed off to StringLiteralConvertible),</div><div class="">- If the behavior can be composed, then the get/materializeForSet/set accessors that project from Self to the base property.</div><div class=""><br class=""></div><div class="">So if you had (using John's syntax proposal):</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">behavior var [foo] name: Value = /*eager*/ initialValue {</div><div class=""> var storage: [Value?!]</div><div class=""> init() { ... }</div><div class=""><br class=""></div><div class=""> accessor foo(x: Int)</div><div class=""> mutating accessor bar(y: String)</div><div class="">}</div><div class=""><br class=""></div></blockquote><div class="">and you instantiated it:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">struct X {</div><div class=""> var [foo] x = 99 {</div><div class=""> foo { ... }</div><div class=""> bar { ... }</div><div class=""> }</div><div class="">}</div><div class=""><br class=""></div></blockquote>we'd emit a data structure like:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">sil_global [let] @"X.x#foo vtable" : $(</div><div class=""> // Project behavior storage from container, *Self -> *Storage</div><div class=""> project_foo: @convention(thin) (RawPointer) -> RawPointer,</div><div class=""> // Accessors</div><div class=""> foo: @convention(method) (Int, @guaranteed X) -> (),</div><div class=""> bar: @convention(method) (String, @inout X) -> (),</div><div class=""> // Initial value</div><div class=""> initialValue: @convention(thin) () -> Int,</div><div class=""> // Name</div><div class=""> name: (RawPointer, Word)</div><div class="">) {</div><div class=""> %project_foo = function_ref @"X.x#foo.project"</div><div class=""> %foo = function_ref @"X.x#foo.foo"</div><div class=""> %bar = function_ref @"X.x#foo.bar"</div><div class=""> %initialValue = function_ref @"X.x#foo.initialValue"</div><div class=""> %name = string_literal utf8 "x"</div><div class=""> %tuple = tuple (%project_foo, %foo, %bar, %initialValue, %name)</div><div class=""> return %tuple</div><div class="">}</div><div class=""><br class=""></div></blockquote>(If we want behaviors to be able to resiliently add accessor requirements, we'll need a more sophisticated vtable with runtime support instead of an ad-hoc global constant, more like a protocol witness table.) Effectively, we treat the behavior like a fragile protocol, albeit one that can be conformed to many times by the same set of types.<div class=""><br class=""></div><div class="">When emitting the behavior's members, we emit them as generic on Self and the property type. Each member implementation receives the container value 'self' (either inout or in_guaranteed, depending on whether it's mutating) and a reference to the behavior vtable as context (again, very similar to a protocol extension method). References to the initializer get lowered to calls to the initializer function in the vtable. References to behavior storage have to be emitted as projections from 'self', and in mutating contexts, should formally be considered formal accesses derived from 'self' in order to be valid 'inout' accesses. References to the name load the raw global string from the vtable and hand it to the contextual type's init(stringLiteral:) initializer.</div><div class=""><br class=""></div><div class="">Inside the type, we instantiate the behavior's storage, and initialize it as if it had an inline initializer calling the behavior's `init`. So:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class="">struct X {</div></div><div class=""><div class=""> var [foo] x = 99 {</div></div><div class=""><div class=""> foo { ... }</div></div><div class=""><div class=""> bar { ... }</div></div><div class=""><div class=""> }</div></div><div class=""><div class="">}</div></div><div class=""><br class=""></div></blockquote>becomes notionally:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class="">struct X {</div></div><div class=""><div class=""> var x#foo.storage: [Int?!] = foo.init(&self, &@"X.x#foo vtable")</div></div><div class=""><div class="">}</div></div></blockquote><div class=""><br class=""></div><div class="">I believe we're able to promote loads from immutable globals, specialize, and inline with existing optimizations, which should allow all this to optimize away, without us having to grow new infrastructure to do AST-level serialization and instantiation, or deviating too much from SILGen's current per-declaration code generation model.</div><div class=""><br class=""></div><div class="">-Joe</div></body></html>