when borrowing an inline type, the value is copied… The idea is inspired by how Swift supports structs
That is conceptually how Swift passes struct args (as well as copying the struct back on return, if it’s a mutable reference), but internally the compiler passes them by pointer.
Copying struct args, plus being unable to mutate individual fields, sounds kind of expensive to me. But I’m willing to admit that might be unwarranted, esp for small structs.
FYI you’re probably familiar with Pony, but I like the way it deals with reference args vs. in-line structs. Although I haven’t written any serious code in Pony.
Copying struct args, plus being unable to mutate individual fields, sounds kind of expensive to me. But I’m willing to admit that might be unwarranted, esp for small structs.
In its current form it’s indeed a bit expensive to borrow an inline type that contains say 10 heap types, because doing so incurs 10 increments. IIRC Swift experiences the same problem when passing structs around that need to be copied, so it’s not entirely a new issue. This is something that we (at some point) should be able to optimize using conventional reference counting optimization techniques.
One approach I thought of is to copy inline types when used as the receiver of a method, and give such methods an extra hidden pointer. Upon each return, the method would first write the receiver copy back to that pointer, then return whatever is supposed to be returned. Using this approach you don’t run into the issue of “sometimes X means a value, other times it means a pointer” and the resulting code generation issues, but you may still fail to observe assignments as outer calls would essentially end up overwriting the writes of inner calls (due to the writes being propagated upon return and not upon assignment).
FYI you’re probably familiar with Pony, but I like the way it deals with reference args vs. in-line structs. Although I haven’t written any serious code in Pony.
Last I checked, Pony only supports structures for FFI purposes and instead puts other types (apart from things like integers and floats) on the heap. Its reference capabilities system is more about access/what you can do and less about how something is passed around or allocated. When looking into what Pony did as part of this work I did find they had some form of escape analysis in the distant past, but I couldn’t figure out if that was still applied and how effective it was.
The copies aren’t deep copies or anything, it’s effectively just a memcpy of the data on the stack. Unless you have a really large type, that shouldn’t be an issue.
As for incrementing: we don’t optimize this at all at the moment, but in the worst case it should perform about the same as traditional reference counting. With optimizations applied, the cost shouldn’t be significant as I suspect most increments can be optimized away.
That is conceptually how Swift passes struct args (as well as copying the struct back on return, if it’s a mutable reference), but internally the compiler passes them by pointer.
Copying struct args, plus being unable to mutate individual fields, sounds kind of expensive to me. But I’m willing to admit that might be unwarranted, esp for small structs.
FYI you’re probably familiar with Pony, but I like the way it deals with reference args vs. in-line structs. Although I haven’t written any serious code in Pony.
In its current form it’s indeed a bit expensive to borrow an inline type that contains say 10 heap types, because doing so incurs 10 increments. IIRC Swift experiences the same problem when passing structs around that need to be copied, so it’s not entirely a new issue. This is something that we (at some point) should be able to optimize using conventional reference counting optimization techniques.
One approach I thought of is to copy inline types when used as the receiver of a method, and give such methods an extra hidden pointer. Upon each return, the method would first write the receiver copy back to that pointer, then return whatever is supposed to be returned. Using this approach you don’t run into the issue of “sometimes X means a value, other times it means a pointer” and the resulting code generation issues, but you may still fail to observe assignments as outer calls would essentially end up overwriting the writes of inner calls (due to the writes being propagated upon return and not upon assignment).
Last I checked, Pony only supports structures for FFI purposes and instead puts other types (apart from things like integers and floats) on the heap. Its reference capabilities system is more about access/what you can do and less about how something is passed around or allocated. When looking into what Pony did as part of this work I did find they had some form of escape analysis in the distant past, but I couldn’t figure out if that was still applied and how effective it was.
This is an interesting approach to ownership / borrowing problems, but I’m a bit concerned about hidden copies and increments.
The copies aren’t deep copies or anything, it’s effectively just a
memcpyof the data on the stack. Unless you have a really large type, that shouldn’t be an issue.As for incrementing: we don’t optimize this at all at the moment, but in the worst case it should perform about the same as traditional reference counting. With optimizations applied, the cost shouldn’t be significant as I suspect most increments can be optimized away.