Using Swift ABI from Rust


#1

Is anybody tracking what Swift is doing with their ABI?

I wonder to what extent an extern "swift" would be possible in Rust. This would be amazing for macOS/iOS, where currently it requires an awkward path of Rust -> C -> ObjC -> Swift (or direct use of ObjC runtime from Rust, which isn’t nice either).

I know Rust’s own stable ABI is far far off, so maybe some Swift interop could also be a way to have some (subset of) stable binary ABI for Rust itself?


#2

How far fetched of an idea is it to imagine an item layout API for the compiler? If a procedural macro crate could provide even a limited #[repr(swift)] or #[repr(cplusplus)], that gives Rust a far better interop story than any language I can think of.


#3

Swift’s implementation of generics is significantly different – there was a talk on this at LLVM’s 2017 dev meeting: https://llvm.org/devmtg/2017-10/#talk15

It seems to me this would be a huge hurdle to getting Rust and Swift talking. Maybe you could do without generics, but then I’m not sure you’re much better than extern "C".


#4

That’s a very helpful link, thanks!

I understood the talk correctly, I still think it should be possible to have some interoperability with Swift’s generics, just without true monomorphisation. Rust’s monomorphised version would just be a still generic-ish code calling Swift’s runtime.

Swift provides “witness tables” for generic types that describe their size, alignment and a sort of vtable for basic operations like copy or destroy. I don’t know if that can be made to agree with Rust’s move semantics. I suppose in the worst case non-Copy objects will all need to be SwiftRc<T>.

I’m not entirely sure how dynamic the tables are. Can the compiler read size/alignment, or is it really so dynamic that it could change at run time later? Lack of const fn size_of could be a problem for Rust. But OTOH Swift does monomorphisation as an optimization.

And finally Swift allows generic types like struct Pair<T> to be treated as opaque types accessed only via witness tables. For Rust that would be a question whether it should be exposed explicitly as methods or whether “direct” field access could be actually dynamic for #[repr(swift)] (desugaring to calls in MIR?).

And finally one thing I’ve noticed that the ABI does not define generic type bounds (where T: Eq). It assumes that for type checking the compiler has access to the source. That’s a bit of a bummer, because that means generics won’t be safe to use only based on ABI alone, and will need equivalent of bindgen to get the bounds.


#5

And the other way, AFAIK, making Rust code usable from Swift requires “just” generating witness tables for everything (including a witness-table-based instantiation of each generic function). It would be the slow dynamic path without monomorphisation. Since Swift doesn’t have borrow checker, possibly only Copy and Arc would be safe to use in this context?


#6

It would be neat if the LLVM cousins (Rust, Swift, Kotlin, etc) could one day in the distant future inter-operate like the JVM languages do.