When should both @inlinable and @inline(__always) be used?

@export is potentially confusing because of collision with the similar notion of "exporting symbols" in C-family compilers. I don't think that's a dealbreaker, but it might be a weak point in favor of something else (and Swift already uses "public" for this notion, mostly).

1 Like

Perhaps:

@exposed(implementation)
@exposed(interface)
2 Likes

Wait, I'm only bringing this up so we don't need to keep repeating this naming exercise for everyone who encounters this feature. I'm not interested in re-bikeshedding names. I'm speaking up on behalf of people who keep coming to me confused. The problem won't go away until the name is changed.

At the time this was discussed on the forum, I did not understand the core team logic. It was only much later that I realized it was a deliberate attempt to hide the functionality of the attribute. So there was a basic problem with how the name was chosen. This is also the reason it took so long to pick a name.

The only way to understand the semantics of library evolution is to understand that functionality: when the compiler produces a binary, it can bake-in assumptions about anything that was exported from other modules, and that binary doesn't need to be recompiled when the exported things change.

If we introduce a feature that controls the semantics of library evolution, then the name needs to be specific to library evolution. The obvious way to do that is to evoke the functionality of the compiler: it exports information that gets baked into other binaries.

Bikeshedding aside, whatever name you pick should not be related to access modifiers, public/protected/private. They exist in a separate domain of semantics. Naturally, higher-level semantic domains imply things about lower-level domains. So, of course a public symbol's interface needs to be exported.

The important thing is that we first know what domain we're talking about.

For the same reason, the name can't have anything to do with inlining, specialization, or anything else in the performance domain. After all, a function certainly does not need the @inlinable attribute to be inlinable! Inlining by definition does not affect program semantics outside the performance domain.

Obviously, functions in a library built with library evolution enabled can't be inlined or specialized if they aren't exported, but that's not the crux of the issue. The compiler can't make any assumptions about those implementations unless that information is explicitily exported!

2 Likes

We use import in a file to mean that we want to use another module's API within that file. Meanwhile, the cross-module overlay proposal is about to formally recognize @_exported as the counterpart for re-exporting modules that are imported.

Since @usableFromInline internal and public differ precisely in that the former declaration isn't available as an API, it seems to me that it'd be unfortunate to use the import/export terminology that means exactly the opposite.

Working with SceneKit recently I've found myself writing code like this:

    let qq = GLKQuaternionMakeWithAngleAndAxis(…)
    let q = SCNQuaternion(qq.x, qq.y, qq.z, qq.w)
    ea.localRotate(by: q)

this led me to write:

extension SCNQuaternion {
    init(_ inQ: GLKQuaternion) {
        self.init(inQ.x, inQ.y, inQ.z, inQ.w)
    }
}

Which immediately gave me the urge to add @inlineable to it, but not really knowing how it works, I decided to google, and found this thread.

Arguably the problem here is that SCNQuaternion lacks a bunch of convenience methods, but should I bother adding @inlineable or @inline(__always) (or both)? Ostensibly the compiler could inline both GLKQuaternionMakeWithAngleAndAxis and the SCNQuaternion construction and then optimize a lot of extra stuff away. Is it likely to do so, even without the annotations?

The general guidance here is to try without it, trace performance, and if there's a specific issue that inlining can resolve, use inlining as needed. If your extension is defined in the same module that you're using it, it probably doesn't matter. It may matter in other circumstances, depending on how it's used and how your project is set up.

This particular init is basically a no-op in disguise, so inlining it won't have most of the downsides that inlining can have, so the risk of being overzealous is pretty small in this case. That's not always true.

7 Likes