SE-0193 - Cross-module inlining and specialization

why can’t we just remove inlineable functions from ABI altogether? if the argument is that app code won’t be able to take advantage of improved implementations in future library versions i don’t think that makes sense at all i would assume client code gets recompiled much more often than library code and their updates are much more likely to be downloaded by users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating the OS might install a new Swift standard library without updating all of your apps.

Slava

···

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
What is your evaluation of the proposal <>?

-1

The proposal puts all the emphasis on the programmer. It is better for the compiler to decide if something is to be inclined both across modules and within modules.

If something is made public then it should be fixed for a given major version number. No need for extra annotation.

A module system that allows versioning is a better solution.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes significant but wrong solution

Does this proposal fit well with the feel and direction of Swift?

No, cluttering up declarations is completely against the clarity of Swift. For example who other than people on this group will understand @inline(never) @inlinable.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes C and C++ and found the equivalent of these annotations problematic. In Java they eliminated all this and let the compiler do the work. In practice this works much better.

Perhaps the compiler should publish the SIL or LLVM for all public functions. Analogous to Java’s class files. This sort of system works really will, much better than C and C++.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed the discussions and read the proposal. The proposal doesn’t seem to encompass all the discussions. It would be nice if the proposal had a much more extensive summary of alternatives suggested.
-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
Reviews are an important part of the Swift evolution process. All review feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager.

When replying, please try to keep the proposal link at the top of the message:

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
...
Reply text
...
Other replies
What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

What is your evaluation of the proposal?

Is the problem being addressed significant enough to warrant a change to Swift?

Does this proposal fit well with the feel and direction of Swift?

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I was making two seperate points: 1. Annotating which parts of a public APIs are stable should not be necessary and 2. Inlining should be left to the compiler.

Point 1: If you had a module system that was versioned then a public declaration should be taken as stable across all minor releases. IE You can only add public declarations, not remove or change, for versions 1.x.x compared to 1.0.0.

That way you don’t need to separately annotate which bits of the API are stable. All the public API is stable.

Point 2: Functions that the compiler of a module decrees are potentially inlinable should be published using SIL or LLVM so that they can be inlined when the module is used either at compile time or runtime. It is important that it is something simple like SIL or LLVM to make inlining light weight. That way the module compiler can be quite speculative about inlining and make many functions available for inlining. In contrast the compiler consuming the library can be conservative with a runtime back up if in doubt. IE if the function is marginal then don’t inline until runtime has proven that inlining is worth while.

-- Howard.

···

On 24 Dec 2017, at 9:53 pm, Slava Pestov <spestov@apple.com> wrote:

On Dec 24, 2017, at 3:04 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

Proposal link: https://github.com/apple/swift-evolution/blob/https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.mdmaster/proposals/0193-cross-module-inlining-and-specialization.md
What is your evaluation of the proposal?

-1

The proposal puts all the emphasis on the programmer. It is better for the compiler to decide if something is to be inclined both across modules and within modules.

If something is made public then it should be fixed for a given major version number. No need for extra annotation.

A module system that allows versioning is a better solution.

Can you explain your proposed solution in more detail?

No, cluttering up declarations is completely against the clarity of Swift. For example who other than people on this group will understand @inline(never) @inlinable.

We don’t expect this attribute to be used frequently in third-party frameworks. @inline(never) @inlinable is a weird combination, but I hope that @inline(never) is used even less frequently. In fact other than debugging it probably doesn’t have much purpose at all, and it would be nice to deprecate it some day.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes C and C++ and found the equivalent of these annotations problematic. In Java they eliminated all this and let the compiler do the work. In practice this works much better.

The Java approach works because there’s no separate compilation — having a JIT means the optimizer is free to inline across module boundaries without any resilience considerations. This doesn’t fit with Swift’s compilation model though.

Slava

in theory this could happen but if you ask me this is such an exceedingly
rare case that i don’t count much net benefit from it. most ithing users
(that i know) avoid ios updates like hell but have automatic app updates
turned on. so 99% of the time i would expect the app version to be more
recent than the library version.

···

On Sun, Dec 24, 2017 at 9:59 PM, Slava Pestov <spestov@apple.com> wrote:

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution < > swift-evolution@swift.org> wrote:

why can’t we just remove inlineable functions from ABI altogether? if the
argument is that app code won’t be able to take advantage of improved
implementations in future library versions i don’t think that makes sense
at all i would assume client code gets recompiled much more often than
library code and their updates are much more likely to be downloaded by
users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating
the OS might install a new Swift standard library without updating all of
your apps.

Slava

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

Proposal link: GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
proposals/0193-cross-module-inlining-and-specialization.md

   -

   What is your evaluation of the proposal?

   -1

   The proposal puts all the emphasis on the programmer. It is better
   for the compiler to decide if something is to be inclined both across
   modules and within modules.

   If something is made public then it should be fixed for a given major
   version number. No need for extra annotation.

   A module system that allows versioning is a better solution.
   -

   Is the problem being addressed significant enough to warrant a change
   to Swift?

   Yes significant but wrong solution
   -

   Does this proposal fit well with the feel and direction of Swift?

   No, cluttering up declarations is completely against the clarity of
   Swift. For example who other than people on this group will understand
   @inline(never) @inlinable.
   -

   If you have used other languages or libraries with a similar feature,
   how do you feel that this proposal compares to those?

   Yes C and C++ and found the equivalent of these annotations
   problematic. In Java they eliminated all this and let the compiler do the
   work. In practice this works much better.

   Perhaps the compiler should publish the SIL or LLVM for all public
   functions. Analogous to Java’s class files. This sort of system works
   really will, much better than C and C++.
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?
   Followed the discussions and read the proposal. The proposal doesn’t
   seem to encompass all the discussions. It would be nice if the proposal had
   a much more extensive summary of alternatives suggested.

-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution < >> swift-evolution@swift.org> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposa
ls/0193-cross-module-inlining-and-specialization.md

Reviews are an important part of the Swift evolution process. All review
feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

When replying, please try to keep the proposal link at the top of the
message:

Proposal link: GitHub - apple/swift: The Swift Programming Language
-evolution/blob/master/proposals/0193-cross-module-inlining-
and-specialization.md
...
Reply text
...
Other replies

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift.

When reviewing a proposal, here are some questions to consider:

   -

   What is your evaluation of the proposal?
   -

   Is the problem being addressed significant enough to warrant a change
   to Swift?
   -

   Does this proposal fit well with the feel and direction of Swift?
   -

   If you have used other languages or libraries with a similar feature,
   how do you feel that this proposal compares to those?
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1. Presumably the portions of A inlined into B and C remain sensitive to the version-specific memory layout of A? Or will ABI stability mean that the compiler can magically rearrange memory offsets in already-compiled code when the layout changes? (Apologies if this is a too-obvious question; this part of Swift is all a mystery to me.)

There is not really a notion of memory layout at the level of an entire module. For structs, classes and enums, you pretty much have the same concerns with both inlinable and non-inlinable functions — if the framework author can change the stored property layout of a struct or class (or adds a case to an enum), code that manipulates these data types must not make any compile-time assumptions that might be invalidated at runtime with a newer version of the framework.

This is basically what the upcoming @fixedContents proposal for structs is about — giving framework authors a way to trade future flexibility for performance by allowing the compiler to make assumptions about the layout of a struct as it is written at compile-time. The @exhaustive proposal for enums has a similar implementation angle, but is of course more interesting because it affects the source language as well, with switch statements.

We don’t plan on any kind of resilience opt-out for classes — already in shipping Swift compilers, accesses to stored properties of classes use accessor methods and not direct access across module boundaries.

Thanks, this is quite helpful.

My underlying concern here is that understanding even what kinds of breakage are _possible_ due to inlining currently requires fairly detailed knowledge of Swift’s guts, and even the best-intentioned among us are going to get it wrong. We’ll need tool help reasoning about it, not just documentation. At least I know _I_ will! (Actually, I’ll probably just avoid @inlinable altogether, but I’d certainly need tool help if I ever do use it.)

2. Is there some class of statically identifiable breaking changes that the compiler does (or should) detect to flag incompatible inlined code? e.g. some version of A inlined into B references A.foo, then A.foo is deleted in a later version of A, so mixing older B with newer A in a project gives a compile- or link-time error?

This is what an “ABI differ” tool would achieve, but like I said it has not yet been designed.

Yes. I would certainly use such a tool if it existed, and not just for dealing with @inlinable.

3. Does this need some sort of poison pill feature for other sorts of breaking changes that are not statically detectable? e.g. invariants of a data structure in A change in release 2.0, so the author of A says “it is an error to include A ≥2.0 in any project that inlined any of my code from a version <2.0.” Is this what you were getting at with the mention of @inlinable(2.0) in the proposal? Sounded like that part was about something else, but I didn’t really grasp it tbh.

This is an interesting point and I think it is outside of the scope of these proposals. If the ABI of a library changes in an incompatible manner and previous binaries are no longer compatible with it, you should think of it as shipping a *new* library, either by changing it’s name or bumping the major version number, so that the dynamic linker prevents the client binary from being run in the first place.

If the compiler/linker actively prohibits mixing of inlined code from different major version numbers, that eases my concern somewhat. A library author isn’t stuck with an ABI-sensitive mistake until the end of time.

Yes, frameworks+app built simultaneously are clearly the more common case. Though Carthage seems to be champing at the bit to create this problem, since it added a feature to download prebuilt binaries long before ABI stability! I can easily imagining this feature spreading via word of mouth as a “secret go faster switch,” and causing no end of problems in the wild.

Perhaps, but I still think it is strictly better to formalize the feature through a proposal and document the pitfalls carefully — the underscored attribute is already spreading through word of mouth and in the absence of official documentation the potential for abuse is greater.

Fair point. Making this feature public & documented, albeit ill understood, is a safety improvement over undocumented & even iller-understood!

It might be safer — and better match the understanding of the typical user — to have @inlinable assume by default that an inlined version of any given method is only valid only for the specific version of the module it was inlined from. The compiler would by default flag any version mixing as an error, and require an explicit statement of compatibility intent for each piece of inlinable code to opt in to the danger zone of mixed versions.

How would this be implemented?

Spitballing (and still out of my depth, so thanks for bearing with me!):

Each inlinable thing comes with a minimum version number for backwards compatibility:

    (syntax is just a placeholder here, could be Chris’s or whatever)

    // module A version 3.0
    @inlinable(≥ 3.0.0) func foo() // introduced in 3.0

    // module A version 3.1
    @inlinable(≥ 3.0.0) func foo() // we maintained compatibility this time

    // module A version 3.5.1
    @inlinable(≥ 3.4.0) func foo() // now dependent on something introduced in 3.4

(Aside: at first I thought this should be a range, but thinking it through, I’m not sure that an _upper_ bound serves any useful purpose.)

Any @inlinable declaration that does not explicitly state a backwards compatibility version in the source code defaults to the current version, i.e. Swift assumes breaking changes every time if the author hasn’t specified otherwise:

    @inlinable func foo() // in source for version 3.5.2

    @inlinable(≥ 3.5.2) func foo() // in ABI

Each module’s ABI specifies both the version number and compatibility version of each item inlined:

    // module B ABI

    requires module A
    inlined func A.foo() from 3.5.1, compatible with ≥ 3.4.0

Or perhaps it would be sufficient to emit only the maximum of the compatibility versions of all the items that were inlined:

    // module B ABI

    requires module A
    inlined some items from 3.5.1, compatible with ≥ 3.4.0

And then the key: at compile/link time, for every framework X, every version of X present — including both inlined versions and the version we’re actually building against — must satisfy the compatibility versions of all the items from X that were inlined by other frameworks.

In other words,

    min(versions of X present) ≥ max(inlined backward compatibility versions of X present).

• • •

This satisfies the three major scenarios I’m pondering:

(1) The stdlib and other similarly brave libraries can support long-term stability of inlined code.

(2) “I just build all my frameworks at the same time” users can use @inlinable safely, without shooting themselves in the foot when version mixing happens.

(3) A team of type #1 can safely make inline-breaking changes or correct mistakes.

• • •

An alternative would be a simpler, coarser-grained policy that says inline compatibility is either (1) same patch version, the default, or (2) same major version, i.e. the only choices are either “I’m not thinking about inline compatibility at all” or “I commit to using semantic versioning even for binary compatibility, not just source compatibility.” That approach smells funny to me, feels like it’s going to be clumsy in practice, but I could see the argument for it.

Cheers,

Paul

···

On Dec 22, 2017, at 1:29 AM, Slava Pestov <spestov@apple.com> wrote:

On Dec 21, 2017, at 12:42 PM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

In short, respectfully request that you at least add this approach to the "alternatives considered” section.

So, does anyone have any strong objections to Chris’s proposal?

From an implementation standpoint, reworking the parser to parse @available(inlinable) and @available(fixedContents) or whatever would be straightforward. I would still like to punt the version range part of this to a future proposal, though.

I wish I had more time to compose a fully thought-out reply, but that's not going to happen in a little while because of outside constraints, so I'll spill a few thoughts here:

No rush, no worries, enjoy the holiday!

I'm not a great fan of the @available(inlinable) notation.

For one, I have a hard time reasoning how Swift would behave when inlinability is tied to OS version. In this example, if the *app* (as opposed to the library) is compiled (as opposed to run) on iOS 16+, then the *library method* would potentially be emitted into the app, but if compiled on iOS 15 it wouldn't? Huh?

No: availability information kicks in based on what you are *deploying* to, not what you’re compiling on.

I expect that this stuff will be extremely rarely used in practice, but here’s an example:

iOS15 declares this public:

public void foo() {
   bar()
}

iOS16 wants to promote foo to inlinable, but knows that the inlined body doesn’t work with iOS15, because iOS15 needs the call to bar to happen (for whatever reason)

@available(inlinable: iOS16)
public void foo() {
// nothing needed on iOS16 or later.
}

The vastly most common case is that something is defined as inlinable and always inlinable, that’s why the @available(inlinable) form is important, and why it may make sense to further sugar that default case to @inlinable.

Second--and perhaps this is not a common opinion--I've always thought that the @available notation was disastrous in terms of readability, especially when it comes to @available(unavailable) and the meaning of the asterisk. Every time, I have to run and look up whether it means the method is in fact available or unavailable for non-listed platforms. Again, with the understanding that this is not a fully formed thought, I have to say that I feel this is taking a readable and fairly straightforward concept (@inlinable) and adding on too many layers of baggage. That it was easy for Swift's creator to inadvertently invert the intended annotations in his initial example is, to me, a pretty good demonstration that the notation is not at all user-friendly.

I agree that @available(unavailable) is a mess, this direction doesn’t make it worse though.

However, the thing I inverted wasn’t the syntax, it was the “abipublic” example. abipublic is a confusing concept no matter how it is spelled.

-Chris

···

On Dec 22, 2017, at 1:03 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

totally agree that these attributes are necessary, they should be in Swift as soon as we bike shedded the right name. But to prevent them from being placed everywhere without thinking I propose to introduce a compiler mode at the same time.
Performance testing is hard and takes time. So I'm pretty sure that as soon as one @abiPublic/@inlinable has proven to improve performance, library authors will just start putting them anywhere as there's no downside besides making the code harder to read.

What is the downside of bringing the attributes and the compiler mode at the same time. Am I massively underestimating the amount of work required for such mode?

ABI stability for the standard library is our top priority for Swift 5. While adding a ‘completely fragile’ compiler mode should be straightforward in theory, the devil is in the details, and I don’t think we can commit to delivering this feature any time soon.

Cool. Just to be sure I understand you correctly: Assuming your proposal gets implemented as proposed, a function that does _not_ have the @inlinable attribute it won't be specialised across modules, ever. Correct?

That is correct. This is already the behavior today, so there’s no change here.

Slava

···

On Dec 22, 2017, at 1:50 AM, Johannes Weiß <johannesweiss@apple.com> wrote:

It still matters if the enum was not “born exhaustive”. Recall that non-exhaustive enums and non-fixed contents structs are address-only types, because we don’t know their size at compile time and so they must be passed indirectly. So if your framework defines a non-exhaustive enum and a function taking or returning this enum, the calling convention will change if the enum becomes exhaustive (unless one of its cases is address-only for some other reason). So we will need to use availability ranges to declare that an enum became exhaustive after the fact. Such an enum will still be treated as non-exhaustive in the calling convention, but can otherwise be manipulated directly (as long as your deployment target is more recent than when it became exhaustive).

Slava

···

On Dec 22, 2017, at 9:55 AM, Chris Lattner <clattner@nondot.org> wrote:

When and if we add private cases to enums, we’ll need to be able to associate an availability range with an “exhaustive” marker. When/if that happens, then yes, we should do so through @available(exhaustive: iOS41, *). The @exhaustive attribute will become sugar for “born exhaustive”, because in the absence of private cases the availability range doesn’t matter (AFAIK).

In short, respectfully request that you at least add this approach to the

"alternatives considered” section.

So, does anyone have any strong objections to Chris’s proposal?

From an implementation standpoint, reworking the parser to parse
@available(inlinable) and @available(fixedContents) or whatever would be
straightforward. I would still like to punt the version range part of this
to a future proposal, though.

I wish I had more time to compose a fully thought-out reply, but that's
not going to happen in a little while because of outside constraints, so
I'll spill a few thoughts here:

No rush, no worries, enjoy the holiday!

I'm not a great fan of the @available(inlinable) notation.

For one, I have a hard time reasoning how Swift would behave when
inlinability is tied to OS version. In this example, if the *app* (as
opposed to the library) is compiled (as opposed to run) on iOS 16+, then
the *library method* would potentially be emitted into the app, but if
compiled on iOS 15 it wouldn't? Huh?

No: availability information kicks in based on what you are *deploying*
to, not what you’re compiling on.

I expect that this stuff will be extremely rarely used in practice, but
here’s an example:

iOS15 declares this public:

public void foo() {
   bar()
}

iOS16 wants to promote foo to inlinable, but knows that the inlined body
doesn’t work with iOS15, because iOS15 needs the call to bar to happen (for
whatever reason)

@available(inlinable: iOS16)
public void foo() {

// nothing needed on iOS16 or later.

}

Deployment platform makes more sense, but I still can't envision a real use
case. What sorts of `bar()` would hypothetically be necessary for iOS 15
but not 16? Why would a third-party library need to increase its inlining
availability for an app based on deployment platform?

The vastly most common case is that something is defined as inlinable and

always inlinable, that’s why the @available(inlinable) form is important,
and why it may make sense to further sugar that default case to @inlinable.

Second--and perhaps this is not a common opinion--I've always thought that
the @available notation was disastrous in terms of readability, especially
when it comes to @available(unavailable) and the meaning of the asterisk.
Every time, I have to run and look up whether it means the method is in
fact available or unavailable for non-listed platforms. Again, with the
understanding that this is not a fully formed thought, I have to say that I
feel this is taking a readable and fairly straightforward concept
(@inlinable) and adding on too many layers of baggage. That it was easy for
Swift's creator to inadvertently invert the intended annotations in his
initial example is, to me, a pretty good demonstration that the notation is
not at all user-friendly.

I agree that @available(unavailable) is a mess, this direction doesn’t
make it worse though.

However, the thing I inverted wasn’t the syntax, it was the “abipublic”
example. abipublic is a confusing concept no matter how it is spelled.

I understand. I was writing about the "abiPublic" example as well, and I
mentioned @available(unavailable) for precisely that reason:

The mess inherent to @available(unavailable) doesn't stem purely from
inelegance in spelling. Rather, @available(anything) looks as though the
"anything" should be a particular kind of API availability, but
@available(unavailable) is _not_ a kind of API availability but precisely
the opposite: unavailability.

I'm quite sure that the reason you inverted your "abiPublic" example is
because of the same issue. Intuitively, you would want to mark something as
"available" in version N and then maybe some special kind of "available" in
version N+1 (which @available(inlinable) would be). But
@available(linkerSymbol), as you spell it, suffers from a similar problem
to that of @available(unavailable): it's _not_ a special kind of API
availability, but rather indicates that something is less-than-available.
That is, you would use it to indicate that something is available as ABI
but not as API. In that sense, it extends the "mess" we have with
@available(unavailable).

···

On Fri, Dec 22, 2017 at 6:12 PM, Chris Lattner <clattner@nondot.org> wrote:

On Dec 22, 2017, at 1:03 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I was making two seperate points: 1. Annotating which parts of a public APIs are stable should not be necessary and 2. Inlining should be left to the compiler.

Point 1: If you had a module system that was versioned then a public declaration should be taken as stable across all minor releases. IE You can only add public declarations, not remove or change, for versions 1.x.x compared to 1.0.0.

That way you don’t need to separately annotate which bits of the API are stable. All the public API is stable.

The @abiPublic attribute as described in the proposal is only meant to be used for *internal* declarations which can be referenced from inlinable functions. Public functions are not annotated, and indeed as you describe, you cannot remove any public declaration if you’re publishing a source-stable or binary-stable library.

Point 2: Functions that the compiler of a module decrees are potentially inlinable should be published using SIL or LLVM so that they can be inlined when the module is used either at compile time or runtime.

The reason the compiler cannot make the decision about what to inline across module boundaries is that this can end up exposing implementation details of the module. If everything can be inlined, you’re effectively doing static linking, which eliminates the ability to ship updated versions of a binary framework.

It is important that it is something simple like SIL or LLVM to make inlining light weight. That way the module compiler can be quite speculative about inlining and make many functions available for inlining. In contrast the compiler consuming the library can be conservative with a runtime back up if in doubt. IE if the function is marginal then don’t inline until runtime has proven that inlining is worth while.

This is already how the optimizer works today. @inlinable annotations are not necessary to inline within a module, where the compiler makes all the decisions based on cost heuristics. Across modules @inlinable does not force any inlining either, it just makes the body available for the optimizer, if it decides to make use of it.

Slava

···

On Dec 24, 2017, at 8:33 PM, Howard Lovatt <howard.lovatt@gmail.com> wrote:

-- Howard.

On 24 Dec 2017, at 9:53 pm, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Dec 24, 2017, at 3:04 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Proposal link: https://github.com/apple/swift-evolution/blob/ <https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md <https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md&gt;master/proposals/0193\-cross\-module\-inlining\-and\-specialization\.md <https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md&gt;What is your evaluation of the proposal <x-apple-data-detectors://7>?

-1

The proposal puts all the emphasis on the programmer. It is better for the compiler to decide if something is to be inclined both across modules and within modules.

If something is made public then it should be fixed for a given major version number. No need for extra annotation.

A module system that allows versioning is a better solution.

Can you explain your proposed solution in more detail?

No, cluttering up declarations is completely against the clarity of Swift. For example who other than people on this group will understand @inline(never) @inlinable.

We don’t expect this attribute to be used frequently in third-party frameworks. @inline(never) @inlinable is a weird combination, but I hope that @inline(never) is used even less frequently. In fact other than debugging it probably doesn’t have much purpose at all, and it would be nice to deprecate it some day.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes C and C++ and found the equivalent of these annotations problematic. In Java they eliminated all this and let the compiler do the work. In practice this works much better.

The Java approach works because there’s no separate compilation — having a JIT means the optimizer is free to inline across module boundaries without any resilience considerations. This doesn’t fit with Swift’s compilation model though.

Slava

Sure, users download new apps more often than they download new OSes, but you’re basically arguing against dynamic linking at this point. If everything was statically linked, vendors would not be able to ship security updates, etc.

Slava

···

On Dec 24, 2017, at 8:46 PM, Kelvin Ma <kelvin13ma@gmail.com> wrote:

in theory this could happen but if you ask me this is such an exceedingly rare case that i don’t count much net benefit from it. most ithing users (that i know) avoid ios updates like hell but have automatic app updates turned on. so 99% of the time i would expect the app version to be more recent than the library version.

On Sun, Dec 24, 2017 at 9:59 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

why can’t we just remove inlineable functions from ABI altogether? if the argument is that app code won’t be able to take advantage of improved implementations in future library versions i don’t think that makes sense at all i would assume client code gets recompiled much more often than library code and their updates are much more likely to be downloaded by users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating the OS might install a new Swift standard library without updating all of your apps.

Slava

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
What is your evaluation of the proposal <>?

-1

The proposal puts all the emphasis on the programmer. It is better for the compiler to decide if something is to be inclined both across modules and within modules.

If something is made public then it should be fixed for a given major version number. No need for extra annotation.

A module system that allows versioning is a better solution.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes significant but wrong solution

Does this proposal fit well with the feel and direction of Swift?

No, cluttering up declarations is completely against the clarity of Swift. For example who other than people on this group will understand @inline(never) @inlinable.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes C and C++ and found the equivalent of these annotations problematic. In Java they eliminated all this and let the compiler do the work. In practice this works much better.

Perhaps the compiler should publish the SIL or LLVM for all public functions. Analogous to Java’s class files. This sort of system works really will, much better than C and C++.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed the discussions and read the proposal. The proposal doesn’t seem to encompass all the discussions. It would be nice if the proposal had a much more extensive summary of alternatives suggested.
-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
Reviews are an important part of the Swift evolution process. All review feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager.

When replying, please try to keep the proposal link at the top of the message:

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
...
Reply text
...
Other replies
What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

What is your evaluation of the proposal?

Is the problem being addressed significant enough to warrant a change to Swift?

Does this proposal fit well with the feel and direction of Swift?

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

aren’t there other benefits to dynamic linking though? i’m not arguing
against dynamic linking, i’m arguing that functions that should be part of
ABI should *always* be called through the entry point and functions that
can be emitted into the client should *never* be called through the entry
point. otherwise it introduces complexity and potential bugs and internally
inconsistent behavior for no obvious benefit. the only reason inlineable
exists is for performance. a library author who marks something inlineable
and then tries to make the implementation itself more efficient is not
going to see much benefit from it just by pushing out the updated library
because no one is going to have the updated library on their device anyway.
we might as well follow Swift’s safety paradigm and make it consistent. as
for security, those functions should never have been marked inlineable to
begin with because even if a new implementation is available (and it won’t)
it doesn’t mean all the call sites will use the updated version.

···

On Sun, Dec 24, 2017 at 11:49 PM, Slava Pestov <spestov@apple.com> wrote:

Sure, users download new apps more often than they download new OSes, but
you’re basically arguing against dynamic linking at this point. If
everything was statically linked, vendors would not be able to ship
security updates, etc.

Slava

On Dec 24, 2017, at 8:46 PM, Kelvin Ma <kelvin13ma@gmail.com> wrote:

in theory this could happen but if you ask me this is such an exceedingly
rare case that i don’t count much net benefit from it. most ithing users
(that i know) avoid ios updates like hell but have automatic app updates
turned on. so 99% of the time i would expect the app version to be more
recent than the library version.

On Sun, Dec 24, 2017 at 9:59 PM, Slava Pestov <spestov@apple.com> wrote:

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution < >> swift-evolution@swift.org> wrote:

why can’t we just remove inlineable functions from ABI altogether? if the
argument is that app code won’t be able to take advantage of improved
implementations in future library versions i don’t think that makes sense
at all i would assume client code gets recompiled much more often than
library code and their updates are much more likely to be downloaded by
users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating
the OS might install a new Swift standard library without updating all of
your apps.

Slava

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote:

Proposal link: https://github.com/apple/swift-evolution/blob/master/p
roposals/0193-cross-module-inlining-and-specialization.md

   -

   What is your evaluation of the proposal?

   -1

   The proposal puts all the emphasis on the programmer. It is better
   for the compiler to decide if something is to be inclined both across
   modules and within modules.

   If something is made public then it should be fixed for a given
   major version number. No need for extra annotation.

   A module system that allows versioning is a better solution.
   -

   Is the problem being addressed significant enough to warrant a
   change to Swift?

   Yes significant but wrong solution
   -

   Does this proposal fit well with the feel and direction of Swift?

   No, cluttering up declarations is completely against the clarity of
   Swift. For example who other than people on this group will understand
   @inline(never) @inlinable.
   -

   If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

   Yes C and C++ and found the equivalent of these annotations
   problematic. In Java they eliminated all this and let the compiler do the
   work. In practice this works much better.

   Perhaps the compiler should publish the SIL or LLVM for all public
   functions. Analogous to Java’s class files. This sort of system works
   really will, much better than C and C++.
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?
   Followed the discussions and read the proposal. The proposal doesn’t
   seem to encompass all the discussions. It would be nice if the proposal had
   a much more extensive summary of alternatives suggested.

-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution < >>> swift-evolution@swift.org> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposa
ls/0193-cross-module-inlining-and-specialization.md

Reviews are an important part of the Swift evolution process. All review
feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

When replying, please try to keep the proposal link at the top of the
message:

Proposal link: GitHub - apple/swift: The Swift Programming Language
-evolution/blob/master/proposals/0193-cross-module-inlining-
and-specialization.md
...
Reply text
...
Other replies

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift.

When reviewing a proposal, here are some questions to consider:

   -

   What is your evaluation of the proposal?
   -

   Is the problem being addressed significant enough to warrant a
   change to Swift?
   -

   Does this proposal fit well with the feel and direction of Swift?
   -

   If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Look like we don’t know the same users.
I don’t know a single user that didn’t update it’s device at least once since he bought it, even if some may avoid the latest update when there device grow old though.

···

Le 25 déc. 2017 à 05:46, Kelvin Ma via swift-evolution <swift-evolution@swift.org> a écrit :

in theory this could happen but if you ask me this is such an exceedingly rare case that i don’t count much net benefit from it. most ithing users (that i know) avoid ios updates like hell but have automatic app updates turned on. so 99% of the time i would expect the app version to be more recent than the library version.

On Sun, Dec 24, 2017 at 9:59 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

why can’t we just remove inlineable functions from ABI altogether? if the argument is that app code won’t be able to take advantage of improved implementations in future library versions i don’t think that makes sense at all i would assume client code gets recompiled much more often than library code and their updates are much more likely to be downloaded by users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating the OS might install a new Swift standard library without updating all of your apps.

Slava

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
What is your evaluation of the proposal <>?

-1

The proposal puts all the emphasis on the programmer. It is better for the compiler to decide if something is to be inclined both across modules and within modules.

If something is made public then it should be fixed for a given major version number. No need for extra annotation.

A module system that allows versioning is a better solution.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes significant but wrong solution

Does this proposal fit well with the feel and direction of Swift?

No, cluttering up declarations is completely against the clarity of Swift. For example who other than people on this group will understand @inline(never) @inlinable.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes C and C++ and found the equivalent of these annotations problematic. In Java they eliminated all this and let the compiler do the work. In practice this works much better.

Perhaps the compiler should publish the SIL or LLVM for all public functions. Analogous to Java’s class files. This sort of system works really will, much better than C and C++.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed the discussions and read the proposal. The proposal doesn’t seem to encompass all the discussions. It would be nice if the proposal had a much more extensive summary of alternatives suggested.
-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
Reviews are an important part of the Swift evolution process. All review feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager.

When replying, please try to keep the proposal link at the top of the message:

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
...
Reply text
...
Other replies
What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

What is your evaluation of the proposal?

Is the problem being addressed significant enough to warrant a change to Swift?

Does this proposal fit well with the feel and direction of Swift?

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

In short, respectfully request that you at least add this approach to the "alternatives considered” section.

So, does anyone have any strong objections to Chris’s proposal?

From an implementation standpoint, reworking the parser to parse @available(inlinable) and @available(fixedContents) or whatever would be straightforward. I would still like to punt the version range part of this to a future proposal, though.

I wish I had more time to compose a fully thought-out reply, but that's not going to happen in a little while because of outside constraints, so I'll spill a few thoughts here:

No rush, no worries, enjoy the holiday!

I'm not a great fan of the @available(inlinable) notation.

For one, I have a hard time reasoning how Swift would behave when inlinability is tied to OS version. In this example, if the *app* (as opposed to the library) is compiled (as opposed to run) on iOS 16+, then the *library method* would potentially be emitted into the app, but if compiled on iOS 15 it wouldn't? Huh?

No: availability information kicks in based on what you are *deploying* to, not what you’re compiling on.

I expect that this stuff will be extremely rarely used in practice, but here’s an example:

iOS15 declares this public:

public void foo() {
   bar()
}

iOS16 wants to promote foo to inlinable, but knows that the inlined body doesn’t work with iOS15, because iOS15 needs the call to bar to happen (for whatever reason)

@available(inlinable: iOS16)
public void foo() {
// nothing needed on iOS16 or later.
}

Deployment platform makes more sense, but I still can't envision a real use case. What sorts of `bar()` would hypothetically be necessary for iOS 15 but not 16? Why would a third-party library need to increase its inlining availability for an app based on deployment platform?

A better example would be if bar() was itself only available in iOS 16:

@available(iOS 15)
@available(inlinable: iOS 16)
public func foo() {
  bar()
}

@available(iOS 16)
public func bar() { … }

Suppose your app calls foo() and deploys to iOS 15. Then you cannot inline foo(), because bar() does not exist on iOS 15. (Presumably, foo() had a different implementation on iOS 15). But if you’re deploying to iOS 16, all is well, and you can inline foo(), which results in your app directly calling bar().

I'm quite sure that the reason you inverted your "abiPublic" example is because of the same issue. Intuitively, you would want to mark something as "available" in version N and then maybe some special kind of "available" in version N+1 (which @available(inlinable) would be). But @available(linkerSymbol), as you spell it, suffers from a similar problem to that of @available(unavailable): it's _not_ a special kind of API availability, but rather indicates that something is less-than-available. That is, you would use it to indicate that something is available as ABI but not as API. In that sense, it extends the "mess" we have with @available(unavailable).

I don’t think it’s quite the same thing as @available(unavailable). An @available(abiPublic) symbol would still be declared to have internal visibility, so in this case the @available attribute makes it strictly more visible than it would be without. We’re not going to spell it as ‘@available(abiPublic) public’, which indeed would be confusing because the symbol is not actually public at the source level.

Slava

···

On Dec 22, 2017, at 7:09 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Dec 22, 2017 at 6:12 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:

On Dec 22, 2017, at 1:03 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Ok, makes sense to me. This is even more reason to provide a framework that scales to these unusual cases. :-)

-Chris

···

On Dec 22, 2017, at 9:08 PM, Slava Pestov <spestov@apple.com> wrote:

On Dec 22, 2017, at 9:55 AM, Chris Lattner <clattner@nondot.org> wrote:

When and if we add private cases to enums, we’ll need to be able to associate an availability range with an “exhaustive” marker. When/if that happens, then yes, we should do so through @available(exhaustive: iOS41, *). The @exhaustive attribute will become sugar for “born exhaustive”, because in the absence of private cases the availability range doesn’t matter (AFAIK).

It still matters if the enum was not “born exhaustive”. Recall that non-exhaustive enums and non-fixed contents structs are address-only types, because we don’t know their size at compile time and so they must be passed indirectly. So if your framework defines a non-exhaustive enum and a function taking or returning this enum, the calling convention will change if the enum becomes exhaustive (unless one of its cases is address-only for some other reason). So we will need to use availability ranges to declare that an enum became exhaustive after the fact. Such an enum will still be treated as non-exhaustive in the calling convention, but can otherwise be manipulated directly (as long as your deployment target is more recent than when it became exhaustive).

Deployment platform makes more sense, but I still can't envision a real use case. What sorts of `bar()` would hypothetically be necessary for iOS 15 but not 16? Why would a third-party library need to increase its inlining availability for an app based on deployment platform?

A better example would be if bar() was itself only available in iOS 16:

@available(iOS 15)
@available(inlinable: iOS 16)
public func foo() {
  bar()
}

@available(iOS 16)
public func bar() { … }

Suppose your app calls foo() and deploys to iOS 15. Then you cannot inline foo(), because bar() does not exist on iOS 15. (Presumably, foo() had a different implementation on iOS 15). But if you’re deploying to iOS 16, all is well, and you can inline foo(), which results in your app directly calling bar().

Thanks, that’s a much better example.

I'm quite sure that the reason you inverted your "abiPublic" example is because of the same issue. Intuitively, you would want to mark something as "available" in version N and then maybe some special kind of "available" in version N+1 (which @available(inlinable) would be). But @available(linkerSymbol), as you spell it, suffers from a similar problem to that of @available(unavailable): it's _not_ a special kind of API availability, but rather indicates that something is less-than-available. That is, you would use it to indicate that something is available as ABI but not as API. In that sense, it extends the "mess" we have with @available(unavailable).

I don’t think it’s quite the same thing as @available(unavailable). An @available(abiPublic) symbol would still be declared to have internal visibility, so in this case the @available attribute makes it strictly more visible than it would be without. We’re not going to spell it as ‘@available(abiPublic) public’, which indeed would be confusing because the symbol is not actually public at the source level.

Right. The bug here is with @available(unavailable). Its design is clearly broken and oxymoronic. That doesn’t make all of @available broken.

-Chris

···

On Dec 22, 2017, at 9:12 PM, Slava Pestov <spestov@apple.com> wrote:

yeah among people i know most ios updates are accidental. unless you count
the one time my friend updated because her phone automatically downloaded
the iso and it was taking up like 5 gb and she had no space left. the last
times i remember anyone willingly updating their iphone was the ios7 update
and the one that gave us all the new emojis. personally mine’s been
pestering me about ios 11.2.1 for a long ass time and i’m actually
relatively good about updating ios because people don’t get the echo text
effect when i send it. also i’m sure the apple slowing down old iphones
news isn’t helping much lol

···

On Mon, Dec 25, 2017 at 4:19 AM, Jean-Daniel <mailing@xenonium.com> wrote:

Look like we don’t know the same users.
I don’t know a single user that didn’t update it’s device at least once
since he bought it, even if some may avoid the latest update when there
device grow old though.

Le 25 déc. 2017 à 05:46, Kelvin Ma via swift-evolution < > swift-evolution@swift.org> a écrit :

in theory this could happen but if you ask me this is such an exceedingly
rare case that i don’t count much net benefit from it. most ithing users
(that i know) avoid ios updates like hell but have automatic app updates
turned on. so 99% of the time i would expect the app version to be more
recent than the library version.

On Sun, Dec 24, 2017 at 9:59 PM, Slava Pestov <spestov@apple.com> wrote:

On Dec 24, 2017, at 4:00 PM, Kelvin Ma via swift-evolution < >> swift-evolution@swift.org> wrote:

why can’t we just remove inlineable functions from ABI altogether? if the
argument is that app code won’t be able to take advantage of improved
implementations in future library versions i don’t think that makes sense
at all i would assume client code gets recompiled much more often than
library code and their updates are much more likely to be downloaded by
users than library updates.

This is not necessarily true. If Swift were to ship with the OS, updating
the OS might install a new Swift standard library without updating all of
your apps.

Slava

On Sun, Dec 24, 2017 at 6:04 PM, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote:

Proposal link: https://github.com/apple/swift-evolution/blob/master/p
roposals/0193-cross-module-inlining-and-specialization.md

   -

   What is your evaluation of the proposal?

   -1

   The proposal puts all the emphasis on the programmer. It is better
   for the compiler to decide if something is to be inclined both across
   modules and within modules.

   If something is made public then it should be fixed for a given
   major version number. No need for extra annotation.

   A module system that allows versioning is a better solution.
   -

   Is the problem being addressed significant enough to warrant a
   change to Swift?

   Yes significant but wrong solution
   -

   Does this proposal fit well with the feel and direction of Swift?

   No, cluttering up declarations is completely against the clarity of
   Swift. For example who other than people on this group will understand
   @inline(never) @inlinable.
   -

   If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

   Yes C and C++ and found the equivalent of these annotations
   problematic. In Java they eliminated all this and let the compiler do the
   work. In practice this works much better.

   Perhaps the compiler should publish the SIL or LLVM for all public
   functions. Analogous to Java’s class files. This sort of system works
   really will, much better than C and C++.
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?
   Followed the discussions and read the proposal. The proposal doesn’t
   seem to encompass all the discussions. It would be nice if the proposal had
   a much more extensive summary of alternatives suggested.

-- Howard.

On 20 Dec 2017, at 7:19 pm, Ted Kremenek via swift-evolution < >>> swift-evolution@swift.org> wrote:

The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposa
ls/0193-cross-module-inlining-and-specialization.md

Reviews are an important part of the Swift evolution process. All review
feedback should be sent to the swift-evolution mailing list at:

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

When replying, please try to keep the proposal link at the top of the
message:

Proposal link: GitHub - apple/swift: The Swift Programming Language
-evolution/blob/master/proposals/0193-cross-module-inlining-
and-specialization.md
...
Reply text
...
Other replies

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift.

When reviewing a proposal, here are some questions to consider:

   -

   What is your evaluation of the proposal?
   -

   Is the problem being addressed significant enough to warrant a
   change to Swift?
   -

   Does this proposal fit well with the feel and direction of Swift?
   -

   If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?
   -

   How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

In Chris's example, it's an annotation of a public API that was once
internal in a previous version:

  @available(apiPublic: iOS 14)
  @available(iOS 15)
  @available(inlinable: iOS 16)
  public func foo() { ... }

This is a sensible use, but it shows how we get to exactly the "indeed
confusing" situation you write about above: here, @available(apiPublic)
elevates the API above internal but below public *even when it annotates a
public API*.

···

On Fri, Dec 22, 2017 at 11:12 PM, Slava Pestov <spestov@apple.com> wrote:

On Dec 22, 2017, at 7:09 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Dec 22, 2017 at 6:12 PM, Chris Lattner <clattner@nondot.org> > wrote:

On Dec 22, 2017, at 1:03 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

In short, respectfully request that you at least add this approach to the

"alternatives considered” section.

So, does anyone have any strong objections to Chris’s proposal?

From an implementation standpoint, reworking the parser to parse
@available(inlinable) and @available(fixedContents) or whatever would be
straightforward. I would still like to punt the version range part of this
to a future proposal, though.

I wish I had more time to compose a fully thought-out reply, but that's
not going to happen in a little while because of outside constraints, so
I'll spill a few thoughts here:

No rush, no worries, enjoy the holiday!

I'm not a great fan of the @available(inlinable) notation.

For one, I have a hard time reasoning how Swift would behave when
inlinability is tied to OS version. In this example, if the *app* (as
opposed to the library) is compiled (as opposed to run) on iOS 16+, then
the *library method* would potentially be emitted into the app, but if
compiled on iOS 15 it wouldn't? Huh?

No: availability information kicks in based on what you are *deploying*
to, not what you’re compiling on.

I expect that this stuff will be extremely rarely used in practice, but
here’s an example:

iOS15 declares this public:

public void foo() {
   bar()
}

iOS16 wants to promote foo to inlinable, but knows that the inlined body
doesn’t work with iOS15, because iOS15 needs the call to bar to happen (for
whatever reason)

@available(inlinable: iOS16)
public void foo() {

// nothing needed on iOS16 or later.

}

Deployment platform makes more sense, but I still can't envision a real
use case. What sorts of `bar()` would hypothetically be necessary for iOS
15 but not 16? Why would a third-party library need to increase its
inlining availability for an app based on deployment platform?

A better example would be if bar() was itself only available in iOS 16:

@available(iOS 15)
@available(inlinable: iOS 16)
public func foo() {
  bar()
}

@available(iOS 16)
public func bar() { … }

Suppose your app calls foo() and deploys to iOS 15. Then you cannot inline
foo(), because bar() does not exist on iOS 15. (Presumably, foo() had a
different implementation on iOS 15). But if you’re deploying to iOS 16, all
is well, and you can inline foo(), which results in your app directly
calling bar().

I'm quite sure that the reason you inverted your "abiPublic" example is
because of the same issue. Intuitively, you would want to mark something as
"available" in version N and then maybe some special kind of "available" in
version N+1 (which @available(inlinable) would be). But
@available(linkerSymbol), as you spell it, suffers from a similar problem
to that of @available(unavailable): it's _not_ a special kind of API
availability, but rather indicates that something is less-than-available.
That is, you would use it to indicate that something is available as ABI
but not as API. In that sense, it extends the "mess" we have with
@available(unavailable).

I don’t think it’s quite the same thing as @available(unavailable). An
@available(abiPublic) symbol would still be declared to have internal
visibility, so in this case the @available attribute makes it strictly more
visible than it would be without. We’re not going to spell it as
@available(abiPublic) public’, which indeed would be confusing because the
symbol is not actually public at the source level.

Random thought: I think this all would make more sense if we rename @available -> @availability and unavailable -> removed.

-Chris

···

On Dec 23, 2017, at 2:59 PM, Chris Lattner <clattner@nondot.org> wrote:

I'm quite sure that the reason you inverted your "abiPublic" example is because of the same issue. Intuitively, you would want to mark something as "available" in version N and then maybe some special kind of "available" in version N+1 (which @available(inlinable) would be). But @available(linkerSymbol), as you spell it, suffers from a similar problem to that of @available(unavailable): it's _not_ a special kind of API availability, but rather indicates that something is less-than-available. That is, you would use it to indicate that something is available as ABI but not as API. In that sense, it extends the "mess" we have with @available(unavailable).

I don’t think it’s quite the same thing as @available(unavailable). An @available(abiPublic) symbol would still be declared to have internal visibility, so in this case the @available attribute makes it strictly more visible than it would be without. We’re not going to spell it as ‘@available(abiPublic) public’, which indeed would be confusing because the symbol is not actually public at the source level.

Right. The bug here is with @available(unavailable). Its design is clearly broken and oxymoronic. That doesn’t make all of @available broken.

Proposal Accepted with Amendment — Naming question

Thank you for everyone's patience on waiting for the Core Team to circle back on this proposal.

This proposal, as well as SE-0192, generated some questions in the Core Team about the general future of library evolution and resilience and the tactical concerns in the near term and the strategic concerns in the long term.

One key discussion point — both on the review thread and in the Core Team — was around library versioning, and how it should inform the design of these attributes. Library versioning is a core piece of the library evolution story, but this proposal specifically calls that out as a future design concern to be revisited. Library versioning is a bigger design topic — and one that probably extends beyond the current Swift 4.2 (and possibly Swift 5) release cycle. The question then is whether to hold these attributes back and wait for a more general scheme that combines library versioning with a broader design for these attributes — and other attributes related to library evolution — or if in their current form they warranted going out as is.

The Core Team felt that:

  • Attributes like @inlinable and @abiPublic (pending spelling revision, see below) will be used widely enough and be useful without versioning information that even if a more generalized scheme comes along for library-related attributes at worst case the spelling of these attributes could be deprecated in the future or just used as a convenient short-hand syntax. Further, many libraries also won't care about library versioning at all, and just want these attributes available to expose performance opportunities for their clients.

  • These attributes would be particularly useful for Swift Package Manager packages, where currently the compilation model provides artificial boundaries to optimization between packages. In the fullness of time the tools should eventually punch through these optimization barriers automatically (when possible) but in the meantime @inlinable is a useful attribute for performance-sensitive cases.

Concern for Abuse of these Attributes

There was some concern (some voiced privately to me as the review manager) that these attributes could be abused. Many library authors may not realize the full implications of marking an API as @inlinable in that in can create interesting scenarios where (a) a client has a copy of a called API inlined but then (b) upgrades to using a newer binary version of a library without rebuilding. In this scenario, it is possible for multiple implementations of the called API (the one in the binary library and the one inlined into the client code) to co-exist. This is a subtle point, and raises the bar for library designers to think the invariants of their exposed APIs.

In the end, these attributes are power tools. Like all power tools they have the potential to be abused and cause injury. Weighing the tradeoffs, the Core Team still felt these were important tools to add to the language.

Next Steps: Naming Revision - @abiPublic

This proposal will essentially be accepted as is — except for the name for @abiPublic. Many people voiced concern for this name. While it technically is accurate, it exposes the term "abi" which will be foreign to many. It also doesn't connote the full implications and value of adding the attribute.

I've heard a few suggested alternate spellings from various people:

  • @inlineAvailable
  • @availableFromInlinable
  • @usableFromInlinable
  • @inlinableCanAccess
  • @inlineAccessibile

All of these have tradeoffs.

Review Extended to March 18, 2018

The proposed spelling for @abiPublic is now @inlineAvailable — but we are officially extending the review a week — to March 15, 2018 — to discuss the spelling of that particular attribute. The rest of the proposal is accepted as specified.

Thank you everyone who participated in this review!

Ted Kremenek
Review Manager

5 Likes

I have trouble with this name. Superficially, just from a language perspective, "inline available" and "inlinable" look like they should be synonyms, which they are not.

Moreover, the name refers to "inline" (a distinct attribute with apparently nonoverlapping functions that I still need to have explained to me repeatedly) rather than "inlinable," and this would increase confusion.

Stepping back to the goals for this name, I disagree that making the attribute explain the "value" of itself is a good approach in general. We don't do this for access modifiers: it's "open" and not "canbesubclassedfromanothermodule," and we don't even do this for "inlinable" (it's not "possiblyMakesYourCodeGoFaster"). Instead, the more honestly we communicate what it's actually doing, the more it will naturally be possible for users to reason about what it does.

I wonder if this attribute would be more accurately a flavor of "public" instead of "internal." Would have to think some more about the most apt name for the idea. It seems related to a request made on this list for modifiers to availability attributes so that it means "available for me but not for you." Ultimately, it seems like if no good design can emerge for this attribute, it's a separable issue in the end that doesn't affect ABI stability, only API design while users have to stick with calling only public methods.

2 Likes