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:

It's an (intentional?) Swift compiler limitation. @usableFromInline says that @inlinable functions can access the _buffer. It exposes the _buffer to the ABI of the NIO module.

The next question is what is @inlinable. It essentially puts the source of a function into the "header file" of the Swift module. I.e. consuming modules have full access to the source. That is very desirable often, because it allows the compiler to optimise calls to it (inline it, etc).

(semantically the _buffer is private (should only be accessed from public functions), but it has to be marked @usableFromInline for performance reasons)

3 Likes

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

Something which may not be obvious is that @usableFromInline is not a replacement for private, it is just that internal is the default, i.e. the

@usableFromInline var _buffer: ByteBuffer

is the same as

@usableFromInline internal var _buffer: ByteBuffer

and this is (IMO very unfortunately) not allowed (but reflects the actual intention, hence the comment):

@usableFromInline private var _buffer: ByteBuffer

(I can get the reasoning, because private somewhat clashes with the property being promoted to the ABI, though that is kinda obvious using @usableFromInline ...)

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.

Yes, anything public already is part of the ABI, so @usableFromInline has no use there. This is about private though.

Those inlinable things are more important in binary (proprietary) frameworks, where you have to be more careful to not accidentally leak module source to the consumer. It is less an issue in Swift FOSS software like NIO (where the compiler has access to the full source anyways).

Can you elaborate? Not sure I understand this part. @usableFromInline affects ABI, private affects access scope. Swift today weirdly merges the two concepts and I think the main motivation is probably no accidental leaking of things private into ABI.

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.

Terms of Service

Privacy Policy

Cookie Policy