Proposal to change the default ownership for passing parameters

It's true that the COW optimization would no longer be possible in your example without explicitly marking the numbers argument as consumed. I don't think our ARC optimizer even reliably makes that happen today, though, even though it should.

Note that, as Erik said, the default convention is still only a default, and the optimizer could still be allowed to do the opposite "guaranteed-to-owned" optimization for non-public functions where it appeared profitable.

Swift and ObjC's ARC semantics have never been sufficient to rely on block scope for RAII. If you need to keep an object alive for a specific duration, you must use withExtendedLifetime to make that explicit.

But sometimes you don't only want to have a long enough lifetime :-) You want to know if deinit has been called or not.) Those are two sides of the same coin, yes. But sometimes an app needs heads, and sometimes it needs tails!

(Assuming withExtendedLifetime only extends the lifetime, and does not make any guarantee on deallocation)

There is no builtin function in the compiler to track array buffer copying. What you can do is to instrument the stdlib, e.g. insert a print in _ContiguousArrayBuffer._copyContents and build your own compiler+stdlib with that instrumentation

Also note that as long as your function is not public and you call it directly (not via a method call), the compiler will be able to automatically convert the parameter form guaranteed to owned. But this is an optimization and you cannot rely on it. So the recommendation is clearly to use the "owned" keyword in this case.

1 Like

A variable's scope puts an upper bound on the referenced object's lifetime, and withExtendedLifetime puts a lower bound. If you do { let x = foo(); withExtendedLifetime(x) { } } it should be fairly safe to assume that x is released at the end of the scope. That still doesn't necessarily guarantee that deinit will happen if x got retained by other references somewhere.

2 Likes

Array.append should be marked as consuming as it wouldn't want the default ownership convention. It's an API that wants to actually store/persist its input.

1 Like

To maximize the opportunity for COW optimization, we may also want the nonmutating forms of mutation operations to explicitly take self consuming as well, since that would give them the opportunity to update a buffer in-place if self hold the only reference to it.

2 Likes

Interesting idea. That would solve the array-problem (from @johannesweiss)

1 Like

Could we expand the use of @escaping for this?

I'm not sure I understood your question, but parameter ownership is something different than @escaping

Hmm. The ownership transfer generally occurs because the ref is escaping the lifetime of the calling stack frame, doesn’t it?

Or if the callee destroys the copy it receives. +1 is better for that as you don't extend the lifetime of the object and if you're the last use of the object it's effectively moved in. "Escaping" is not a great word for it as this use case is more like a black hole, and nothing escapes from a black hole.

1 Like

Hi Eric,

Sorry for the delay getting back to you. IMO your plan makes perfect sense, I'd love to see this, and I agree with your special case for setters. I expect it falls out of this change, but please make sure that varargs list arguments are also +0 values :-) Thanks!

-Chris

It would certainly be best if the array was passed +0, but the current code-generation pattern on varargs does require all the elements to be copied so that they can be stored into the array.

The expressions passed into the varargs list are only used by that varargs list, so they should be movable, no?

EDIT: Oh, right, that's only if the expression is produced as a +1 value itself. If it is a borrowed value, then this doesn't work. The right model is probably a borrowed array for varargs. How's ownership going? :-)

-Chris

Heh. To answer seriously: for move-only parameters, the tentative plan is that we'll require functions to be explicit about ownership, because it's very semantically important. (We can continue to default methods to borrowing, of course.) The ideal result for varargs would be to do the same thing: basically, either the entire varargs array is borrowed or owned. The biggest problem there is that I don't really know how to make an array of borrowed values! That's not something that our intentionally-simplified ownership system actually supports; we'd really need the varargs collection to be a more abstracted collection type than just Array, something where we can easily kick in arbitrary code to do a copy if the user requests one (which, given an array of actually move-only types, they can't, but presumably this also should work as an optimization for copyable types).

Ok, right. I agree that there isn't a simple way to avoid copying the elements in the general case. It seems reasonable to give up that hope, it will be no worse than our +1 arguments currently are at least.

Will arrays of move-only types work? Will we be able to pass move-only types through varargs?

Yeah. The array becomes move-only itself, of course.

We should still look into making it so that varargs don't pass an Array at the ABI boundary too.

1 Like

+1!!