Question about usable from inline in SwiftNIO

Hi everyone!

I was looking around in Swift NIO source code and I found something like this a couple of times:

/* private but usableFromInline */ @usableFromInline var _buffer: ByteBuffer

It is used both in ByteBufferView and in Scheduled<T>.

I was wondering if someone could be so nice to explain the reason behind this to a noob in compiler optimization like me and why @usableFromInline is preferred to private in these cases.

Cheers :pray:t2:

This is a great question! To answer it thoroughly we need to take a digression into the Swift optimiser and optimisation boundaries, but the TL;DR is that @usableFromInline is required to make substantial chunks of NIO perform better.

The more detailed answer here is that methods (including property accessors) in Swift can be marked as @inlinable. The effect of doing this is that it makes the implementation of the method part of the public interface of the module. In modules that export a stable ABI this is a substantial commitment, as it is no longer the method itself that is part of the ABI, but it is the entire body of the method. Anything relied on by the body of the method cannot be changed without breaking the ABI.

To help drive this home, Swift requires that anything touched by an @inlinable method must meet one of the following requirements:

  1. Be public; OR
  2. Be @inlinable internal; OR
  3. Be @usableFromInline internal

The intent of this is to make it clear that any such declaration is part of the public ABI and cannot safely be changed.

Note that neither @inlinable or @usableFromInline can be applied to a private or fileprivate declaration. There are some complex reasons for why to do with symbol name clashes that are largely not worth getting into here (basically, things that are @usableFromInline must be unique in the module, which clashes with the expected behaviour of private/fileprivate). As a result, NIO is forced to choose: its preferred symbol visibility, or the ability to be used in an inlinable method.

So why do we care about @inlinable?

The answer is that Swift cannot optimise code across module boundaries. This includes generic specialization, but it also includes optimisations around refcounts and ownership and a number of other things. For this reason, we pay careful attention to places where inlinable declarations make sense. This is particularly common in our core data structures.

3 Likes

FWIW, @usableFromInline is essentially used only on internal. public already implies usable from inline, and fileprivate or smaller (currently) cannot be usable from inline. So the goldilocks spot contains only internal.

If we allow @usableFromInline private, it'd mean moving file is also ABI breaking which sounds... weird. Ah well, no point arguing in a clarification thread.

Private declarations that are public ABI don't make much sense given incongruent uniqueness requirements. The ways to resolve it are to disallow the clashing of the declarations (prefer the public part) or use file names to disambiguate the declarations (prefer the private part). I was talking about the latter case. This is not the right place to pitch a private @usableFromInline, so it's haughty of me to assume one direction or another, apologies.

I think it's more about guarding against the unintentional usage of declarations while also making them available form optimization. Public ABI still gives about the same amount of information so it won't really help with leakage concern.