associated objects

I agree Karl, especially the bit about the difficulty of implementation
being the limiting thing here.

I think the language devs must have some idea how this will work, but don't
seem to want to share/discuss it at the moment. I was hoping for some
feedback about my implementation ideas - whether they are along the right
lines, or way off, or not necessary (because the implementation strategy is
already known). Perhaps this the wrong list for that kind of discussion?

Anyway, I was thinking some more and I had another idea about
implementation. This could be very useful for maintaining binary
compatability in other areas too.

Implementation idea No. 4:

The basic concept is that the dynamic linker would fixup the offsets as
well as relocating the addersses, allowing the size of objects (and maybe
structs?) to change at link-time. The process might be something like this:

* References to members defined in extensions would compile to have an
offset symbol instead of a value - so they can be fixed up later
* The linker would scan all the shared objects that are referenced (and
thus might get linked)
* Build up a list of Stored Properties In ExtensionS (SPIES, muhahaha) for
each class.
* Append the extra fields (increase the size the class), decide where each
member goes in the extended layout and fixup the offsets
* Carry on with normal relocation

There are quite a few assumptions in the above, and probably quite a few
misunderstandings about how things work on my part too (I'm not an expert
at this), however I think it should work in principle. Some questions about
my assumptions: Can linker know in advance all the potential modules that
could be linked, or is this done more lazily and it only knows about what
it's linking right now? Is it ok for the size to change - I don't know if
it's a static sizeof() or if it could be (or already is) stored in the isa?

Would love to hear back from anyone who is familiar with the code if this
sounds viable or not. I'd love to go digging in there myself (I've tried a
few times but got lost) but I don't have time at the moment.

···

On Tue, 11 Oct 2016 at 08:19 Karl <razielim@gmail.com> wrote:

> On 10 Oct 2016, at 21:15, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Right. The question is whether we *need* to add stored properties
out-of-module, and what the use case for that is. To me it seems that
adding them in-module is by far the more common use case, for the purposes
of implementing protocols.
>
> At any rate, the rewrite option would be a great addition to Swift
regardless of what our answer to the first question is.
>
> Charles

I actually think out-of-module is the more common and important case. If
you’re using a types from an external framework, it’s strange that you can
retroactively add functionality but not additional data. That limitation,
AFAICT, is purely due to implementation. Some well-constructed frameworks
will allow you to add stored data to the types being used by subclassing.
Not all are so well constructed, and as we have more and more value-types
subclassing is not going to be a solution.

You can add stored properties to objects in Python, which is one of the
really great things about it. If you need to add a stored property to track
some additional state you layer on top of a framework’s components, it’s
staggering how easy it is - you don’t even need to define any new types.

I personally think it’s important for Swift as a scripting language to
have this ability - for value-types as well as classes, by default. We
could optimise it away completely for types which aren’t public, and you
should be able to explicitly declare a type non-extendable (similar to
declaring it non-subclassable with ‘final’) to opt-out. I’m sure it will
never happen, but there you go.

I’m haven’t read the implementation, but as I see it, “weak” has the effect of storing associated values. Can that mechanism be made generic?

···

+1 from me too for internal extensions; I think this is the logical place to start for now so we can see how many use cases this actually covers. Personally I don't try to add functionality to types from other libraries, and when I have to I try as much as possible to do it via wrapping; obviously this doesn't cover the async type cases and others where it'd be hard to re-wrap the values, but I think I'd like to know more about how common such cases actually are.

However, allowing stored properties in local extensions is an absolute must for a first step, and would be very useful as a first step.

The problem I have with doing it for external types is that I just don't see how it can be done efficiently; associated objects means looking up the object to find what its associated values are, which isn't a negligible cost if you're doing it frequently, and it makes me very uncomfortable to think of hiding what is actually happening, as developers may think they're just using a regular property without really appreciating the actual costs involved. At the very least they need to handled through a .associated property or whatever to make it much clearer that these aren't native properties in the normal sense.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Currently, we can expect that fields of a type are all collected in the
same place. This expectation applies more for "static" and less for
"dynamic" types.
So I agree with the idea for @objc classes, strongly disagree for structs,
and disagree for regular classes.

Perhaps some types don’t lend themselves to being extended?

Intuitively I would think any extensions should not affect the core
behaviour at all. So if I extended a type by adding a property x, two
instances with everything else the same and different values of x should
still be considered equal by a type-specific equality check. For example
you would agree that 1 + 1 = 2 but what if the numbers were coloured red,
blue, and yellow respectively, like fridge-magnets, should 1(red) + 1(blue)
= 2(yellow)? I think yes. The colour is an extension, it doesn’t change the
fundamental concept or behaviour of an integer number.

I see extensions as a way to add functionality (and potentially data), but
without affecting the core behaviour. If you wanted to change behaviour
then you should use inheritance or composition to create something new. You
can’t then use your own type for instances created by a library, unless it
gives you a way to do that, the library would expect its own types to
behave in a predictable way, similarly they should behave the same way when
extended.

···

On Thu, 3 Nov 2016 at 15:14 Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

Has anybody thought about the semantic issues of out-of-module extensions
with stored properties apart from the implementation issues?

Such properties could potentially wreak havoc with the semantics of the
type being extended. How would these properties play nice with an existing
definition of equality, for example? How can it be guaranteed that their
value is consistent with the remaining state? And kept that way in case of
mutability?

-Thorsten

> Am 15.10.2016 um 03:01 schrieb Paul Cantrell via swift-evolution < > swift-evolution@swift.org>:
>
>
>> On Oct 9, 2016, at 3:43 PM, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Let extensions introduce stored properties, but only in the same module
as the type’s definition. Then, the compiler can just take any extensions
into consideration when it’s determining the size of the type, just as if
the properties had been declared in the type. Declaring stored properties
on an extension outside of the type’s module results in a compiler error,
exactly as today. This would, without any performance drawbacks, solve one
of the big problems that people are hoping to solve via stored properties
in extensions—the ability to organize members by protocol conformance.
>
> Yes please! A big strong +1 to this from me. I can think of several
specific chunks of problem code that this would clean up immensely.
>
> Contra Karl in another message, it’s _in-module_ stored property
extensions that I want most frequently. By far.
>
> It seems to me that Charles’s idea could be introduced as its own
proposal. If out-of-module stored property extensions do eventually become
feasible, then Charles’s proposal is a good stepping stone. If they never
do, then his proposal has done no harm.
>
> I realize this probably falls into the post-ABI stability bucket, but
I’d love to help write/support the proposal when its time comes.
>
> Cheers,
>
> Paul
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Implementation idea No. 4:

The basic concept is that the dynamic linker would fixup the offsets as well as relocating the addersses, allowing the size of objects (and maybe structs?) to change at link-time. The process might be something like this:

* References to members defined in extensions would compile to have an offset symbol instead of a value - so they can be fixed up later
* The linker would scan all the shared objects that are referenced (and thus might get linked)
* Build up a list of Stored Properties In ExtensionS (SPIES, muhahaha) for each class.
* Append the extra fields (increase the size the class), decide where each member goes in the extended layout and fixup the offsets
* Carry on with normal relocation

There are quite a few assumptions in the above, and probably quite a few misunderstandings about how things work on my part too (I'm not an expert at this), however I think it should work in principle. Some questions about my assumptions: Can linker know in advance all the potential modules that could be linked, or is this done more lazily and it only knows about what it's linking right now? Is it ok for the size to change - I don't know if it's a static sizeof() or if it could be (or already is) stored in the isa?

This sort of scheme isn't dynamic enough. The worst-case is a extension in a library that gets dlopen()ed at runtime on a class that is already loaded. The build-time linker can't know anything about it. The loader and the runtime will see it, but at that point the class may already be in use and may already have instances allocated. If you want to handle the dlopen() case then you need some way to add storage to arbitrary objects that have already been allocated.

Ole Begemann wrote:

For what it's worth, Greg Parker (Cc'ed) started a discussion back in March that I think is relevant here: [swift-dev] [Discussion] New refcount representation

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other per-object data. This is an outline of the scheme. Comments and suggestions welcome.

Today, each object stores 64-bits of refcounts and flags after the isa field.

In this new system, each object would store a pointer-size field after the isa field. This field would have two cases: it could store refcounts and flags, or it could store a pointer to a side allocation that would store refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like associated references or class extensions with instance variables.
…"

I don't know the current status of this idea (implemented? planned? abandoned?). Also, it's worth noting that this would only apply to classes, not value types.

I'm working on this right now: https://github.com/gparker42/swift/tree/new-refcount-representation

If it goes well it will provide the runtime implementation space needed for associated objects or stored properties in extensions. Such storage would be less efficient than "real" stored properties. Any object with that storage attached would also suffer additional performance penalties to refcounting and deallocation. On the plus side there is no memory penalty to objects that don't have additional storage, and there is no contention over a big global association table like there is in Objective-C's associated object implementation.

Note that the runtime implementation is not the only problem. The optimizer folks hate the fact that stored properties in extensions defeat the compiler's visibility into the deinit behavior of all types, even if most types are unaffected at runtime.

···

On Oct 11, 2016, at 3:02 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

I think the language devs must have some idea how this will work, but

> don't seem to want to share/discuss it at the moment. I was hoping for
> some feedback about my implementation ideas - whether they are along the
> right lines, or way off, or not necessary (because the implementation
> strategy is already known). Perhaps this the wrong list for that kind of
> discussion?

For what it's worth, Greg Parker (Cc'ed) started a discussion back in March that I think is relevant here: [swift-dev] [Discussion] New refcount representation

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other per-object data. This is an outline of the scheme. Comments and suggestions welcome.

Today, each object stores 64-bits of refcounts and flags after the isa field.

In this new system, each object would store a pointer-size field after the isa field. This field would have two cases: it could store refcounts and flags, or it could store a pointer to a side allocation that would store refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like associated references or class extensions with instance variables.
…"

I don't know the current status of this idea (implemented? planned? abandoned?). Also, it's worth noting that this would only apply to classes, not value types.

Ole

Greg:

I've CCed you in case you want to respond to my comments below about
potentially optimising "extensionIvar" access without using a dictionary.
See my response to Haravikk below.

Benjamin:

Implementation wise, weak does *not* currently have the effect of storing
associated values. It does however mean that any object with weak
references stays allocated after being deinited, until all the weak
references are evaluated and zeroed (they are not zeroed when the object
deinits, zeroing is done lazily. See
https://www.mikeash.com/pyblog/friday-qa-2015-12-11-swift-weak-references.html
for a detailed discussion).

However, this seems likely to change at some point when Greg's changes are
merged. Weakly referenced objects would cause a side-table to be allocated,
with the benefits that the object could be deallocated immediately after
deinit, and only the side-table would hang around (to service attempts to
access weak references, which would still be lazily zeroed). The small
disadvantage of this (which only applies to instances that actually have
had weak references) is that an extra pointer dereference is needed for
retain, release, and weak reference access (and some other things). But a
big advantage is that the side-allocation could be used for other things
too, like stored properties.

Haravikk:

It can be done efficiently using Greg's proposed changes. The current
implementation on his branch (
https://github.com/gparker42/swift/tree/new-refcount-representation\) does
not have any extra space for stored properties, but he has discussed
"extensionIvars" before on swift-dev, and proposed that the side-table
structure contains a pointer to a dictionary for these. However, I think
with some dynamic loader magic this could be implemented as a dynamic
structure instead of a dictionary. Each time a module is loaded, the side
allocation for stored properties could be extended and the offsets to the
newly extended properties could be fixed-up based on the current size.
Existing instances could be handled by using the structure size as a
version number (stored at the beginning of this area), it would check if
the instance is at the current version and migrate/update the structure as
needed (realloc it and init the extended area to zero, updating the
size/version field). These checks would be less overhead than a
getter/setter function call, so using dot notation to access the properties
would not be deceiving programmers about the cost.

Anton:

Why should it matter where data is stored? Can you expand on any reasons
for wanting object data to be contiguous, or thinking that it shouldn't be
allowed for Swift classes?

This line of thought suggests that allowing stored properties for same-module extensions is not only much more feasible to implement, but also makes more sense at the user level.

···

On Nov 10, 2016, at 4:36 AM, Jay Abbott <jay@abbott.me.uk> wrote:

Perhaps some types don’t lend themselves to being extended?

Intuitively I would think any extensions should not affect the core behaviour at all. So if I extended a type by adding a property x, two instances with everything else the same and different values of x should still be considered equal by a type-specific equality check. For example you would agree that 1 + 1 = 2 but what if the numbers were coloured red, blue, and yellow respectively, like fridge-magnets, should 1(red) + 1(blue) = 2(yellow)? I think yes. The colour is an extension, it doesn’t change the fundamental concept or behaviour of an integer number.

I see extensions as a way to add functionality (and potentially data), but without affecting the core behaviour. If you wanted to change behaviour then you should use inheritance or composition to create something new. You can’t then use your own type for instances created by a library, unless it gives you a way to do that, the library would expect its own types to behave in a predictable way, similarly they should behave the same way when extended.

On Thu, 3 Nov 2016 at 15:14 Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Has anybody thought about the semantic issues of out-of-module extensions with stored properties apart from the implementation issues?

Such properties could potentially wreak havoc with the semantics of the type being extended. How would these properties play nice with an existing definition of equality, for example? How can it be guaranteed that their value is consistent with the remaining state? And kept that way in case of mutability?

-Thorsten

> Am 15.10.2016 um 03:01 schrieb Paul Cantrell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
>
>
>> On Oct 9, 2016, at 3:43 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> Let extensions introduce stored properties, but only in the same module as the type’s definition. Then, the compiler can just take any extensions into consideration when it’s determining the size of the type, just as if the properties had been declared in the type. Declaring stored properties on an extension outside of the type’s module results in a compiler error, exactly as today. This would, without any performance drawbacks, solve one of the big problems that people are hoping to solve via stored properties in extensions—the ability to organize members by protocol conformance.
>
> Yes please! A big strong +1 to this from me. I can think of several specific chunks of problem code that this would clean up immensely.
>
> Contra Karl in another message, it’s _in-module_ stored property extensions that I want most frequently. By far.
>
> It seems to me that Charles’s idea could be introduced as its own proposal. If out-of-module stored property extensions do eventually become feasible, then Charles’s proposal is a good stepping stone. If they never do, then his proposal has done no harm.
>
> I realize this probably falls into the post-ABI stability bucket, but I’d love to help write/support the proposal when its time comes.
>
> Cheers,
>
> Paul
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Wow, that's a very interesting post. Sounds a lot simpler to implement than
my idea about fixing up offsets in the linker and preserves binary
compatability just the same.

I got some complaints when I first started talking about this that the
runtime would have to track extra pointers and performance would be
affected because of additional levels of indirection, which I didn't fully
understand because it seems fairly trivial to me. Perhaps this twin-purpose
refcount/side-table pointer also suffers the same concerns?

> I think the language devs must have some idea how this will work, but

> don't seem to want to share/discuss it at the moment. I was hoping for

> some feedback about my implementation ideas - whether they are along the

> right lines, or way off, or not necessary (because the implementation

> strategy is already known). Perhaps this the wrong list for that kind of

> discussion?

For what it's worth, Greg Parker (Cc'ed) started a discussion back in

March that I think is relevant here:

https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20160314/001424.html

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other

per-object data. This is an outline of the scheme. Comments and

suggestions welcome.

Today, each object stores 64-bits of refcounts and flags after the isa

field.

In this new system, each object would store a pointer-size field after

the isa field. This field would have two cases: it could store refcounts

and flags, or it could store a pointer to a side allocation that would

store refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like

associated references or class extensions with instance variables.

…"

I don't know the current status of this idea (implemented? planned?

abandoned?). Also, it's worth noting that this would only apply to

classes, not value types.

Ole

···

On Wed, 12 Oct 2016 at 00:21 Ole Begemann via swift-evolution < swift-evolution@swift.org> wrote:

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

Interestingly, if all the stored properties defined in extensions could be
determined at link-time, the size/layout of the side-table could include
them directly, removing the need for further indirection. The offsets would
still need to be fixed up, which would add some complexity to the
implementation.

···

On Wed, 12 Oct 2016 at 00:54 Jay Abbott <jay@abbott.me.uk> wrote:

Wow, that's a very interesting post. Sounds a lot simpler to implement
than my idea about fixing up offsets in the linker and preserves binary
compatability just the same.

I got some complaints when I first started talking about this that the
runtime would have to track extra pointers and performance would be
affected because of additional levels of indirection, which I didn't fully
understand because it seems fairly trivial to me. Perhaps this twin-purpose
refcount/side-table pointer also suffers the same concerns?

On Wed, 12 Oct 2016 at 00:21 Ole Begemann via swift-evolution < > swift-evolution@swift.org> wrote:

> I think the language devs must have some idea how this will work, but

> don't seem to want to share/discuss it at the moment. I was hoping for

> some feedback about my implementation ideas - whether they are along the

> right lines, or way off, or not necessary (because the implementation

> strategy is already known). Perhaps this the wrong list for that kind of

> discussion?

For what it's worth, Greg Parker (Cc'ed) started a discussion back in

March that I think is relevant here:

[swift-dev] [Discussion] New refcount representation

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other

per-object data. This is an outline of the scheme. Comments and

suggestions welcome.

Today, each object stores 64-bits of refcounts and flags after the isa

field.

In this new system, each object would store a pointer-size field after

the isa field. This field would have two cases: it could store refcounts

and flags, or it could store a pointer to a side allocation that would

store refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like

associated references or class extensions with instance variables.

…"

I don't know the current status of this idea (implemented? planned?

abandoned?). Also, it's worth noting that this would only apply to

classes, not value types.

Ole

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

That's great! I suppose the idea of allocating a bit of extra storage for similar data in value types is some sort of heresy?

Would there be a conceptual reason for that; which explains why it's okay for reference-types but not for values? Personally I feel like it's a kind of C legacy, due to performance and layout expectations that C sets about value-types ¯\_(ツ)_/¯

Karl

···

Sent from my iPad

On 12 Oct 2016, at 07:54, Greg Parker <gparker@apple.com> wrote:

On Oct 11, 2016, at 3:02 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Implementation idea No. 4:

The basic concept is that the dynamic linker would fixup the offsets as well as relocating the addersses, allowing the size of objects (and maybe structs?) to change at link-time. The process might be something like this:

* References to members defined in extensions would compile to have an offset symbol instead of a value - so they can be fixed up later
* The linker would scan all the shared objects that are referenced (and thus might get linked)
* Build up a list of Stored Properties In ExtensionS (SPIES, muhahaha) for each class.
* Append the extra fields (increase the size the class), decide where each member goes in the extended layout and fixup the offsets
* Carry on with normal relocation

There are quite a few assumptions in the above, and probably quite a few misunderstandings about how things work on my part too (I'm not an expert at this), however I think it should work in principle. Some questions about my assumptions: Can linker know in advance all the potential modules that could be linked, or is this done more lazily and it only knows about what it's linking right now? Is it ok for the size to change - I don't know if it's a static sizeof() or if it could be (or already is) stored in the isa?

This sort of scheme isn't dynamic enough. The worst-case is a extension in a library that gets dlopen()ed at runtime on a class that is already loaded. The build-time linker can't know anything about it. The loader and the runtime will see it, but at that point the class may already be in use and may already have instances allocated. If you want to handle the dlopen() case then you need some way to add storage to arbitrary objects that have already been allocated.

Ole Begemann wrote:

For what it's worth, Greg Parker (Cc'ed) started a discussion back in March that I think is relevant here: [swift-dev] [Discussion] New refcount representation

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other per-object data. This is an outline of the scheme. Comments and suggestions welcome.

Today, each object stores 64-bits of refcounts and flags after the isa field.

In this new system, each object would store a pointer-size field after the isa field. This field would have two cases: it could store refcounts and flags, or it could store a pointer to a side allocation that would store refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like associated references or class extensions with instance variables.
…"

I don't know the current status of this idea (implemented? planned? abandoned?). Also, it's worth noting that this would only apply to classes, not value types.

I'm working on this right now: https://github.com/gparker42/swift/tree/new-refcount-representation

If it goes well it will provide the runtime implementation space needed for associated objects or stored properties in extensions. Such storage would be less efficient than "real" stored properties. Any object with that storage attached would also suffer additional performance penalties to refcounting and deallocation. On the plus side there is no memory penalty to objects that don't have additional storage, and there is no contention over a big global association table like there is in Objective-C's associated object implementation.

Note that the runtime implementation is not the only problem. The optimizer folks hate the fact that stored properties in extensions defeat the compiler's visibility into the deinit behavior of all types, even if most types are unaffected at runtime.

--
Greg Parker gparker@apple.com Runtime Wrangler

How much would convenient method forwarding when wrapping change how
desirable this feature is? I am hesitant to add storage to already
allocated instances if we can avoid it.

···

On Sun, Oct 16, 2016 at 5:02 PM, Jay Abbott via swift-evolution < swift-evolution@swift.org> wrote:

Greg:

I've CCed you in case you want to respond to my comments below about
potentially optimising "extensionIvar" access without using a dictionary.
See my response to Haravikk below.

Benjamin:

Implementation wise, weak does *not* currently have the effect of storing
associated values. It does however mean that any object with weak
references stays allocated after being deinited, until all the weak
references are evaluated and zeroed (they are not zeroed when the object
deinits, zeroing is done lazily. See https://www.mikeash.com/
pyblog/friday-qa-2015-12-11-swift-weak-references.html for a detailed
discussion).

However, this seems likely to change at some point when Greg's changes are
merged. Weakly referenced objects would cause a side-table to be allocated,
with the benefits that the object could be deallocated immediately after
deinit, and only the side-table would hang around (to service attempts to
access weak references, which would still be lazily zeroed). The small
disadvantage of this (which only applies to instances that actually have
had weak references) is that an extra pointer dereference is needed for
retain, release, and weak reference access (and some other things). But a
big advantage is that the side-allocation could be used for other things
too, like stored properties.

Haravikk:

It can be done efficiently using Greg's proposed changes. The current
implementation on his branch (gparker42 (Greg Parker) · GitHub
swift/tree/new-refcount-representation) does not have any extra space for
stored properties, but he has discussed "extensionIvars" before on
swift-dev, and proposed that the side-table structure contains a pointer to
a dictionary for these. However, I think with some dynamic loader magic
this could be implemented as a dynamic structure instead of a dictionary.
Each time a module is loaded, the side allocation for stored properties
could be extended and the offsets to the newly extended properties could be
fixed-up based on the current size. Existing instances could be handled by
using the structure size as a version number (stored at the beginning of
this area), it would check if the instance is at the current version and
migrate/update the structure as needed (realloc it and init the extended
area to zero, updating the size/version field). These checks would be less
overhead than a getter/setter function call, so using dot notation to
access the properties would not be deceiving programmers about the cost.

Anton:

Why should it matter where data is stored? Can you expand on any reasons
for wanting object data to be contiguous, or thinking that it shouldn't be
allowed for Swift classes?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Benjamin:

Implementation wise, weak does *not* currently have the effect of storing associated values. It does however mean that any object with weak references stays allocated after being deinited, until all the weak references are evaluated and zeroed (they are not zeroed when the object deinits, zeroing is done lazily. See mikeash.com: Friday Q&A 2015-12-11: Swift Weak References for a detailed discussion).

However, this seems likely to change at some point when Greg's changes are merged. Weakly referenced objects would cause a side-table to be allocated, with the benefits that the object could be deallocated immediately after deinit, and only the side-table would hang around (to service attempts to access weak references, which would still be lazily zeroed). The small disadvantage of this (which only applies to instances that actually have had weak references) is that an extra pointer dereference is needed for retain, release, and weak reference access (and some other things). But a big advantage is that the side-allocation could be used for other things too, like stored properties.

Thanks for the reference to the article. I always meant to take the time to do that bit spelunking, never actually had the time, and the non-code documentation I found claimed it was out of date so I didn't bother reading it. Of all the possible implementations I conjectured after hearing about ARC when it was announced for Obj-C, this was not one of them. And frankly, reading it makes me want to quit working as a software developer and take up writing psychological thrillers as my job. :(

The implementation in Objective-C ARC was not the same as Swift's, IIRC. I believe that it zeroed the references out immediately at the time the object ran out of references, rather than doing it lazily the next time something tried to access the reference. I could be wrong, though.

Charles

···

On Oct 16, 2016, at 6:56 PM, Benjamin Spratling via swift-evolution <swift-evolution@swift.org> wrote:

Benjamin:

Implementation wise, weak does *not* currently have the effect of storing associated values. It does however mean that any object with weak references stays allocated after being deinited, until all the weak references are evaluated and zeroed (they are not zeroed when the object deinits, zeroing is done lazily. See mikeash.com: Friday Q&A 2015-12-11: Swift Weak References for a detailed discussion).

However, this seems likely to change at some point when Greg's changes are merged. Weakly referenced objects would cause a side-table to be allocated, with the benefits that the object could be deallocated immediately after deinit, and only the side-table would hang around (to service attempts to access weak references, which would still be lazily zeroed). The small disadvantage of this (which only applies to instances that actually have had weak references) is that an extra pointer dereference is needed for retain, release, and weak reference access (and some other things). But a big advantage is that the side-allocation could be used for other things too, like stored properties.

Thanks for the reference to the article. I always meant to take the time to do that bit spelunking, never actually had the time, and the non-code documentation I found claimed it was out of date so I didn't bother reading it. Of all the possible implementations I conjectured after hearing about ARC when it was announced for Obj-C, this was not one of them. And frankly, reading it makes me want to quit working as a software developer and take up writing psychological thrillers as my job. :(

T.J.: This would not replace the need for stored properties in extensions.
For example: Imagine adding a property to UIView in an extension, then
recursively traversing a view hierarchy and accessing the property. This
thought experiment should quickly show you why wrapping and adding
properties (or even subclassing and adding properties) simply won't work,
even if an easy forwarding mechanism were in place. You do not have control
of the creation of all those objects, so you can't make them your type, and
even if you could, you would have to provide a wrapper class for every
UIView subclass, and then what if another module also wants to do the same?

However, note that it would be possible to only add the extra storage to
instances where some additional properties are actually accessed/in-use.
The pointer for the stored property data in the side-table would simply be
nil if it has never been used.

···

On Sun, 16 Oct 2016 at 23:31 T.J. Usiyan <griotspeak@gmail.com> wrote:

How much would convenient method forwarding when wrapping change how
desirable this feature is? I am hesitant to add storage to already
allocated instances if we can avoid it.

On Sun, Oct 16, 2016 at 5:02 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

Greg:

I've CCed you in case you want to respond to my comments below about
potentially optimising "extensionIvar" access without using a dictionary.
See my response to Haravikk below.

Benjamin:

Implementation wise, weak does *not* currently have the effect of storing
associated values. It does however mean that any object with weak
references stays allocated after being deinited, until all the weak
references are evaluated and zeroed (they are not zeroed when the object
deinits, zeroing is done lazily. See
mikeash.com: Friday Q&A 2015-12-11: Swift Weak References
for a detailed discussion).

However, this seems likely to change at some point when Greg's changes are
merged. Weakly referenced objects would cause a side-table to be allocated,
with the benefits that the object could be deallocated immediately after
deinit, and only the side-table would hang around (to service attempts to
access weak references, which would still be lazily zeroed). The small
disadvantage of this (which only applies to instances that actually have
had weak references) is that an extra pointer dereference is needed for
retain, release, and weak reference access (and some other things). But a
big advantage is that the side-allocation could be used for other things
too, like stored properties.

Haravikk:

It can be done efficiently using Greg's proposed changes. The current
implementation on his branch (
https://github.com/gparker42/swift/tree/new-refcount-representation\) does
not have any extra space for stored properties, but he has discussed
"extensionIvars" before on swift-dev, and proposed that the side-table
structure contains a pointer to a dictionary for these. However, I think
with some dynamic loader magic this could be implemented as a dynamic
structure instead of a dictionary. Each time a module is loaded, the side
allocation for stored properties could be extended and the offsets to the
newly extended properties could be fixed-up based on the current size.
Existing instances could be handled by using the structure size as a
version number (stored at the beginning of this area), it would check if
the instance is at the current version and migrate/update the structure as
needed (realloc it and init the extended area to zero, updating the
size/version field). These checks would be less overhead than a
getter/setter function call, so using dot notation to access the properties
would not be deceiving programmers about the cost.

Anton:

Why should it matter where data is stored? Can you expand on any reasons
for wanting object data to be contiguous, or thinking that it shouldn't be
allowed for Swift classes?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Karl, interesting point... perhaps a similar scheme could work for value
types (using the COW refcount)?

···

On Thu, 13 Oct 2016 at 16:02 Karl Wagner <razielim@gmail.com> wrote:

That's great! I suppose the idea of allocating a bit of extra storage for
similar data in value types is some sort of heresy?

Would there be a conceptual reason for that; which explains why it's okay
for reference-types but not for values? Personally I feel like it's a kind
of C legacy, due to performance and layout expectations that C sets about
value-types ¯\_(ツ)_/¯

Karl

Sent from my iPad

On 12 Oct 2016, at 07:54, Greg Parker <gparker@apple.com> wrote:

On Oct 11, 2016, at 3:02 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

Implementation idea No. 4:

The basic concept is that the dynamic linker would fixup the offsets as
well as relocating the addersses, allowing the size of objects (and maybe
structs?) to change at link-time. The process might be something like this:

* References to members defined in extensions would compile to have an
offset symbol instead of a value - so they can be fixed up later
* The linker would scan all the shared objects that are referenced (and
thus might get linked)
* Build up a list of Stored Properties In ExtensionS (SPIES, muhahaha) for
each class.
* Append the extra fields (increase the size the class), decide where each
member goes in the extended layout and fixup the offsets
* Carry on with normal relocation

There are quite a few assumptions in the above, and probably quite a few
misunderstandings about how things work on my part too (I'm not an expert
at this), however I think it should work in principle. Some questions about
my assumptions: Can linker know in advance all the potential modules that
could be linked, or is this done more lazily and it only knows about what
it's linking right now? Is it ok for the size to change - I don't know if
it's a static sizeof() or if it could be (or already is) stored in the isa?

This sort of scheme isn't dynamic enough. The worst-case is a extension in
a library that gets dlopen()ed at runtime on a class that is already
loaded. The build-time linker can't know anything about it. The loader and
the runtime will see it, but at that point the class may already be in use
and may already have instances allocated. If you want to handle the
dlopen() case then you need some way to add storage to arbitrary objects
that have already been allocated.

Ole Begemann wrote:

For what it's worth, Greg Parker (Cc'ed) started a discussion back in
March that I think is relevant here:
https://lists.swift.org/pipermail/swift-dev/Week-
of-Mon-20160314/001424.html

Here's the relevant part:

"I am considering a new representation for Swift refcounts and other
per-object data. This is an outline of the scheme. Comments and suggestions
welcome.

Today, each object stores 64-bits of refcounts and flags after the isa
field.

In this new system, each object would store a pointer-size field after the
isa field. This field would have two cases: it could store refcounts and
flags, or it could store a pointer to a side allocation that would store
refcounts and flags and additional per-object data.

Advantages:

* Allows inexpensive per-object storage for future features like
associated references or class extensions with instance variables.
…"

I don't know the current status of this idea (implemented? planned?
abandoned?). Also, it's worth noting that this would only apply to classes,
not value types.

I'm working on this right now:
https://github.com/gparker42/swift/tree/new-refcount-representation

If it goes well it will provide the runtime implementation space needed
for associated objects or stored properties in extensions. Such storage
would be less efficient than "real" stored properties. Any object with that
storage attached would also suffer additional performance penalties to
refcounting and deallocation. On the plus side there is no memory penalty
to objects that don't have additional storage, and there is no contention
over a big global association table like there is in Objective-C's
associated object implementation.

Note that the runtime implementation is not the only problem. The
optimizer folks hate the fact that stored properties in extensions defeat
the compiler's visibility into the deinit behavior of all types, even if
most types are unaffected at runtime.

--
Greg Parker gparker@apple.com Runtime Wrangler