Not to pick nits early, but a few points here:
- I wasn't sure exactly what you meant by “types with reference semantics,” so I went to look at examples (since @Alvae, @shabalin, @dan-zheng, @saeta, and I have been trying to formalize value semantics, how these terms are used matters to me).
- I think it's unlikely these types actually have reference semantics in the way we intend the term.
Expr
, for example, is non-copyable, and almost every non-copyable type has value semantics by our definition: given a variable of that type, you can only change its value via operations on that variable. - Giving these types from inside the Swift compiler as examples of what you mean is a bit sub-optimal for the audience. For example, I took a look at
SILInstruction
and while it obviously has deleted copy-assignment anddelete
operators, many of its properties are probably a consequence of its inheritance fromllvm::ilist_node<SILInstruction>
… (which has four base classes of its own!), making it tough to get a read on what you're saying. It would be better if you'd give some minimal examples.
Currently, these types cannot be imported at all, because we don't have a way to materialize them or copy them around. Foreign reference types allow these C++ "reference types" to be imported with ergonomic Swift semantics, this makes them easy to use without unsafe pointers.
I'm gonna guess that what you really mean here is “non-copyable type.” But if that's not right I hope you will enlighten me.
Foreign reference types (or custom reference types) actually have a broad set of applications beyond just C++ interop, but I won’t get into that in this post.
I agree, but I doubt the specific things you're trying to handle with this initial, limited proposal should be imported as any kind of reference type. Of course, being really sure about that requires a more rigorous nailing-down of what these types have in common…
From my point-of-view, just dealing with the non-copyable subset of types that I assume you're trying to address:
- We'll need non-copyable types eventually in Swift, so we'd better spell out their semantics in a way that isn't interop-specific.
- C++ types with nontrivial copy constructors should be imported as non-copyable by default. All Swift types have O(1) copy/assignment operations; allowing things like
std::set<std::vector<std::string>>
to be implicitly copied from Swift would introduce some frightening performance cliffs. - Special annotation should be available to exempt things like
shared_ptr
that have a nontrivial but O(1) copy, so they're copyable in Swift. - Non-copyable types should be passable-by-value like any other type. Passing by value doesn't imply a copy or a transfer of ownership; you can think of non-copyable types as using a guaranteed calling convention, or being passed by pointer-to-immutable.
@escaping
annotation should be available for all parameters. You can use it whenever a non-copyable parameter value might need to outlive the callee frame; typically the parameter would have to be movable for that to work.- Passing a non-copyable type as an
@escaping
parameter ends its lifetime in the calling function. If you want to keep using it, you need to use an explicit copy (if one is available). - Non-copyable types are passable-by-
inout
like any other type. - Imported nontrivial types that are copyable in C++ should be given an explicit copy API by the importer.
There's an implicit contract that goes with passing by &
or const&
in C++: unless otherwise specified, the callee is going to assume that it has a unique reference to the value. In other words, &
corresponds to Swift's inout
and const&
corresponds Swift's pass-by-value in the common C++ programming model. The places where these assumptions do not hold are extremely rare, in part because they require awkward hoop-jumping in specification (you can see some examples in the C++ standard library). It would be a cryin' shame if we didn't take advantage of this correspondence for interop.