[RFC] "Library Evolution Support in Swift ('Resilience')"

I don't think there's a silver bullet here. Even if the implementation is inside the binary, you'll still have clients with vendored or statically linked copies of old versions of your library embedded in their distributions. Clients always have to share in the responsibility for maintaining their security.

-Joe

···

On Feb 10, 2016, at 6:08 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

I think the solution is that you shouldn't mark security-critical code as being eligible for inlining. Anything else is just not going to be workable.

There is no such thing as "I solemnly swear this function doesn't have a security bug, and if it does, we'll never patch it."

If we have to choose between security and inlining, let's not allow inlining across versioned APIs.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

When someone vendors or statically links a library, they know what they are getting into. I mean, sometimes people do silly things, but they should know.

Whereas "inlining across dynamically linked libraries" is a novel Swiftism (Or it may be a Rustism as well but in any case) nobody expects it. They will assume that replacing the dynamic library actually replaces the dynamic library's code, because that is the social contract for dynamic linkage.

More broadly, I see inlining dynamic libraries as a "nice to have", but dynamic linkage that actually works as "critical". This proposal inverts that value system, telling me that I will get inlined performance, and I might get a correctly linked program. That seems backwards.

Or stated another way: if I can't relink my program afterwards, why am I using dynamic linkage? I should statically link everything.

Let me advance an independent argument why I think this is the wrong behavior: LGPL compliance. Eliding LGPLv3 §4)d)1:

You may convey a Combined Work... if you... Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.

and §4)e:

Provide Installation Information... to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version.

I suspect that linking an LGPLed library under this proposals' semantics may be a lawsuit foot-gun.

...inb4 "don't use the LGPL", but I think this (and the security problem) generalize to a very broad class of issues. Dynamic linkage has a certain "social contract" since time immemorial. Breaking this contract has consequences that I am not sure we have thought through entirely.

···

On Feb 10, 2016, at 9:46 PM, Joe Groff <jgroff@apple.com> wrote:

I don't think there's a silver bullet here. Even if the implementation is inside the binary, you'll still have clients with vendored or statically linked copies of old versions of your library embedded in their distributions. Clients always have to share in the responsibility for maintaining their security.

-Joe

On Feb 10, 2016, at 6:08 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

I think the solution is that you shouldn't mark security-critical code as being eligible for inlining. Anything else is just not going to be workable.

There is no such thing as "I solemnly swear this function doesn't have a security bug, and if it does, we'll never patch it."

If we have to choose between security and inlining, let's not allow inlining across versioned APIs.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

OK, I can be more correct: nondeterministic behavior. At least it will be consistent in the path that happens. =)

···

On Feb 10, 2016, at 1:07 PM, Jordan Rose <jordan_rose@apple.com> wrote:

I do object to the word "undefined", which in the C family of languages means "the compiler can do anything it wants". In this case you will definitely get behavior equivalent to the old code, or equivalent to the new code, and the section on optimizing inlineable functions makes it clear that the inlineable code must not make assumptions about what library it's deployed against. Again, memory and type safety are preserved, while under "undefined behavior" in a C language they definitely would not be.

Jordan

On Feb 10, 2016, at 13:00, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

Actually, there are quite a few places that allow for the same type of behavior: @inlineable, accessors, enums, structs, etc...

As soon as I see these words: "existing clients may use the new implementations, or they may use the implementations from the time they were compiled, or a mix of both" or similar, we've ventured right back into undefined territory.

That concerns me.

-David

On Feb 10, 2016, at 12:45 PM, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 8, 2016, at 6:24 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This last point is a specific case of a general tenet of Swift: the default behavior is safe. Where possible, choices made when an entity is first published should not limit its evolution in the future.

Regarding this:

Changing or removing a default value is permitted but discouraged; it may break or change the meaning of existing source code.

Maybe I'm being dense, but how is something with a caveat of "discouraged" and "it may break or change" in-line with "the default behavior is safe"? I've got no qualms with putting the default value in the client code; I actually think that is fine.

However, my concern is that you will have different behavior depending on if you simply drop in the updated binary vs recompile against the binary. Even worse if you have multiple components within your app that link against the library. If only one of those components is recompiled on release, you now have a problem of conflicting behavior within your own app because of the client-side calling the API will be using different values.

It would seem that removing the default value could be permitted (though maybe still discouraged) because this will result in a compiler error in the scenario above. It's still possible to have different behavior in your components, but now it's no longer implicitly happening. However, changing the default parameter seems highly problematic.

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

C and C++ do it too, of course: pretty much anything you put in a C or C++ header file is fair game for inlining. The implementations of dispatch_once() and NSMakeRect() are two examples off the top of my head. Nothing you do to libdispatch.dylib or Foundation.framework will affect a previously-compiled caller of those functions.

Yes, @inlineable must be applied with care. The capability is too useful - especially for systems-level programming - to go without it.

···

On Feb 10, 2016, at 10:49 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

When someone vendors or statically links a library, they know what they are getting into. I mean, sometimes people do silly things, but they should know.

Whereas "inlining across dynamically linked libraries" is a novel Swiftism (Or it may be a Rustism as well but in any case) nobody expects it. They will assume that replacing the dynamic library actually replaces the dynamic library's code, because that is the social contract for dynamic linkage.

--
Greg Parker gparker@apple.com Runtime Wrangler

I don't think anyone is arguing that @inlineable shouldn't be available, but we are already heading down the path of "some features" are more important than resilience, so we'll allow it. That seems to be defeat purpose of resiliency in the first place.

I personally think it's a very fair trade-off to disallow @inlineable code to be inlined from a dynamic dll by default. Now, that doesn't mean that the developer couldn't add a link flag `--super-nonresilient-i-know-what-im-doing` when pulling in certain DLLs. That's an explicit statement that the developer is choosing to break resilience. The important part is that it's the developer consuming the DLL making the choice vs. the developer of the DLL making that choice for everyone.

Further, this can now be a runtime warning/error when loading a version of the DLL that is not the same one that was linked against originally. Another option is to hash the inlined function and if those hashes don't match error.

-David

···

On Feb 10, 2016, at 11:04 PM, Greg Parker via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 10, 2016, at 10:49 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When someone vendors or statically links a library, they know what they are getting into. I mean, sometimes people do silly things, but they should know.

Whereas "inlining across dynamically linked libraries" is a novel Swiftism (Or it may be a Rustism as well but in any case) nobody expects it. They will assume that replacing the dynamic library actually replaces the dynamic library's code, because that is the social contract for dynamic linkage.

C and C++ do it too, of course: pretty much anything you put in a C or C++ header file is fair game for inlining. The implementations of dispatch_once() and NSMakeRect() are two examples off the top of my head. Nothing you do to libdispatch.dylib or Foundation.framework will affect a previously-compiled caller of those functions.

Yes, @inlineable must be applied with care. The capability is too useful - especially for systems-level programming - to go without it.

+2

IMO disabling inlining of DLLs should clearly be the default, and there should be some "I know what I'm doing" flag for folks who are willing to take on the burden of vendoring their library as if it were static.

···

On Feb 11, 2016, at 11:53 AM, David Owens II <david@owensd.io> wrote:

I personally think it's a very fair trade-off to disallow @inlineable code to be inlined from a dynamic dll by default. Now, that doesn't mean that the developer couldn't add a link flag `--super-nonresilient-i-know-what-im-doing` when pulling in certain DLLs. That's an explicit statement that the developer is choosing to break resilience. The important part is that it's the developer consuming the DLL making the choice vs. the developer of the DLL making that choice for everyone.

You're using inlineable code today in Cocoa: dispatch_once's fast-path, the geometry functions from CoreGraphics to SceneKit, and every NS_OPTIONS enum that has an "all" case. It is a performance vs. flexibility tradeoff, but an incredibly important one. In Swift we definitely don't want to sacrifice flexibility by default, but it has to be an option if we want Swift to be a viable replacement for things traditionally done with C or C++. "Inlining across dynamic libraries" has been around for decades with C, C++, and C#.

I'm skeptical that anyone will actually use your hypothetical "use this library without treating anything as fragile, even if I could" setting; most clients just want as much performance as they can get. But it would be implementable.

Jordan

···

On Feb 11, 2016, at 13:54, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 11, 2016, at 11:53 AM, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

I personally think it's a very fair trade-off to disallow @inlineable code to be inlined from a dynamic dll by default. Now, that doesn't mean that the developer couldn't add a link flag `--super-nonresilient-i-know-what-im-doing` when pulling in certain DLLs. That's an explicit statement that the developer is choosing to break resilience. The important part is that it's the developer consuming the DLL making the choice vs. the developer of the DLL making that choice for everyone.

+2

IMO disabling inlining of DLLs should clearly be the default, and there should be some "I know what I'm doing" flag for folks who are willing to take on the burden of vendoring their library as if it were static.

Do you have any metrics on how often the disable safety metrics are used when compiling swift code?

C++ isn't know for being the bastion of doing versioning correctly. I agree it has the speed target that hopefully Swift can get to.

As for C#, unless you are talking about something I don't know about, ngen and JIT (runtime) are the only ways that the code will be inlined. However, when the assembly changes, that should invalidate the ngen'd image forcing it to be regenerated. Hence the entire feature for the TargetedPatchingOptOut on system framework dlls (selective replacement of the ngen'd images).

In any case, for apps, the scenario is largely irrelevant. To ship and update to that, I must do a recompile. This is mostly true for all ship mechanism on Apple platforms now any because.

Maybe it's a narrow enough surface area where this would actually be a practical issue such that it's largely irrelevant...

-David

···

On Feb 11, 2016, at 2:39 PM, Jordan Rose <jordan_rose@apple.com> wrote:

On Feb 11, 2016, at 13:54, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 11, 2016, at 11:53 AM, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

I personally think it's a very fair trade-off to disallow @inlineable code to be inlined from a dynamic dll by default. Now, that doesn't mean that the developer couldn't add a link flag `--super-nonresilient-i-know-what-im-doing` when pulling in certain DLLs. That's an explicit statement that the developer is choosing to break resilience. The important part is that it's the developer consuming the DLL making the choice vs. the developer of the DLL making that choice for everyone.

+2

IMO disabling inlining of DLLs should clearly be the default, and there should be some "I know what I'm doing" flag for folks who are willing to take on the burden of vendoring their library as if it were static.

You're using inlineable code today in Cocoa: dispatch_once's fast-path, the geometry functions from CoreGraphics to SceneKit, and every NS_OPTIONS enum that has an "all" case. It is a performance vs. flexibility tradeoff, but an incredibly important one. In Swift we definitely don't want to sacrifice flexibility by default, but it has to be an option if we want Swift to be a viable replacement for things traditionally done with C or C++. "Inlining across dynamic libraries" has been around for decades with C, C++, and C#.

I'm skeptical that anyone will actually use your hypothetical "use this library without treating anything as fragile, even if I could" setting; most clients just want as much performance as they can get. But it would be implementable.

When I generate a Swift interface, does the @inlineable attribute appear for the function?

If so, I think you've all made good arguments here.

Well, Apple itself will be using this model, so if there's a bug in an inlineable function in Cocoa, this could make a difference.

Unfortunately, I can't share any metrics about -O vs. -Ounchecked.

Jordan

···

On Feb 11, 2016, at 15:56, David Owens II <david@owensd.io> wrote:

On Feb 11, 2016, at 2:39 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

On Feb 11, 2016, at 13:54, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 11, 2016, at 11:53 AM, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

I personally think it's a very fair trade-off to disallow @inlineable code to be inlined from a dynamic dll by default. Now, that doesn't mean that the developer couldn't add a link flag `--super-nonresilient-i-know-what-im-doing` when pulling in certain DLLs. That's an explicit statement that the developer is choosing to break resilience. The important part is that it's the developer consuming the DLL making the choice vs. the developer of the DLL making that choice for everyone.

+2

IMO disabling inlining of DLLs should clearly be the default, and there should be some "I know what I'm doing" flag for folks who are willing to take on the burden of vendoring their library as if it were static.

You're using inlineable code today in Cocoa: dispatch_once's fast-path, the geometry functions from CoreGraphics to SceneKit, and every NS_OPTIONS enum that has an "all" case. It is a performance vs. flexibility tradeoff, but an incredibly important one. In Swift we definitely don't want to sacrifice flexibility by default, but it has to be an option if we want Swift to be a viable replacement for things traditionally done with C or C++. "Inlining across dynamic libraries" has been around for decades with C, C++, and C#.

I'm skeptical that anyone will actually use your hypothetical "use this library without treating anything as fragile, even if I could" setting; most clients just want as much performance as they can get. But it would be implementable.

Do you have any metrics on how often the disable safety metrics are used when compiling swift code?

C++ isn't know for being the bastion of doing versioning correctly. I agree it has the speed target that hopefully Swift can get to.

As for C#, unless you are talking about something I don't know about, ngen and JIT (runtime) are the only ways that the code will be inlined. However, when the assembly changes, that should invalidate the ngen'd image forcing it to be regenerated. Hence the entire feature for the TargetedPatchingOptOut on system framework dlls (selective replacement of the ngen'd images).

In any case, for apps, the scenario is largely irrelevant. To ship and update to that, I must do a recompile. This is mostly true for all ship mechanism on Apple platforms now any because.

Maybe it's a narrow enough surface area where this would actually be a practical issue such that it's largely irrelevant...

If it does, what are you going to do about it? Are you going to try to make a distinction on a per-function basis whether or not it's safe to inline it?

I can understand a client wanting to say wholesale not to use any inlineable code from library X, but if you care about being able to update inlineable parts of the library I don't think I trust any client to decide which parts of the library are "safe, probably".

Jordan

···

On Feb 11, 2016, at 14:54, Drew Crawford <drew@sealedabstract.com> wrote:

When I generate a Swift interface, does the @inlineable attribute appear for the function?

I'm trying to ship security code in Swift on Linux.

I agree that on iOS it doesn't matter, but in terms of the broader computing ecosystem that distribution model is the exception and not the rule.

···

On Feb 11, 2016, at 5:56 PM, David Owens II <david@owensd.io> wrote:

Maybe it's a narrow enough surface area where this would actually be a practical issue such that it's largely irrelevant...

If it does, what are you going to do about it? Are you going to try to make a distinction on a per-function basis whether or not it's safe to inline it?

Backing up, the argument was advanced that inlining is fine because it's equivalent to C/C++. C/C++ has a way to determine with certainty if a function will be inlined (e.g. appears in the header) so either we are equivalent to C/C++ and we can also determine with certainty or we are not. If we are not, then arguments of the form "C/C++ does it" when actually it has a similar but subtly different feature begin to lose ground.

But to answer the question about "what I would do about it", I might not call the function, if I knew it to be inlined. Another thing I have done in C/C++ is vendor the inline function, and implement it slightly more optimally based on things I know at the callsite, so that is a special case of not calling it.

I don't think I trust any client to decide which parts of the library are "safe, probably"

Similarly, I don't trust library authors to decide which parts of their libraries are safe for me to inline in my client.

I think this may be a difference in perspective, e.g. if one works at Apple then one may receive many bug reports of the form "silly app developer, don't use UIKit this way" leading to a general mistrust of clients. Meanwhile, if one has hundreds of filed radars still open (ಠ_ಠ) one might develop a general mistrust of library authors.

To me it seems that this trust problem could be resolved were inlining to be an opt-in feature for each party. The library author should opt-in at the function level, and the client should opt in at least at the library level, and I would prefer at the callsite.

···

On Feb 11, 2016, at 6:26 PM, Jordan Rose <jordan_rose@apple.com> wrote:

Sounds like you'll already need to plan for a `TargetedPatchingOptOut` feature then... it's a good question on how apps that consume inlineable framework code get updates without a recompile, especially for critical security fixes.

-David

···

On Feb 11, 2016, at 4:19 PM, Jordan Rose <jordan_rose@apple.com> wrote:

On Feb 11, 2016, at 15:56, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

Do you have any metrics on how often the disable safety metrics are used when compiling swift code?

C++ isn't know for being the bastion of doing versioning correctly. I agree it has the speed target that hopefully Swift can get to.

As for C#, unless you are talking about something I don't know about, ngen and JIT (runtime) are the only ways that the code will be inlined. However, when the assembly changes, that should invalidate the ngen'd image forcing it to be regenerated. Hence the entire feature for the TargetedPatchingOptOut on system framework dlls (selective replacement of the ngen'd images).

In any case, for apps, the scenario is largely irrelevant. To ship and update to that, I must do a recompile. This is mostly true for all ship mechanism on Apple platforms now any because.

Maybe it's a narrow enough surface area where this would actually be a practical issue such that it's largely irrelevant...

Well, Apple itself will be using this model, so if there's a bug in an inlineable function in Cocoa, this could make a difference.

Unfortunately, I can't share any metrics about -O vs. -Ounchecked.

If it does, what are you going to do about it? Are you going to try to make a distinction on a per-function basis whether or not it's safe to inline it?

Backing up, the argument was advanced that inlining is fine because it's equivalent to C/C++. C/C++ has a way to determine with certainty if a function will be inlined (e.g. appears in the header) so either we are equivalent to C/C++ and we can also determine with certainty or we are not. If we are not, then arguments of the form "C/C++ does it" when actually it has a similar but subtly different feature begin to lose ground.

That's true, but it still doesn't mean all clients will use the same implementation. If different client libraries are compiled against different versions of a base library, they will each inline the version they were compiled with, and of course the base library itself will use the version it was compiled with. Knowing for sure that your client is inlining isn't extra information over knowing it may inline.

(Technically, a C function marked 'inline' and not 'static' isn't guaranteed to be inlined either, but in practice nobody does that because the rules are awful.)

But to answer the question about "what I would do about it", I might not call the function, if I knew it to be inlined. Another thing I have done in C/C++ is vendor the inline function, and implement it slightly more optimally based on things I know at the callsite, so that is a special case of not calling it.

There may not be another way to accomplish what you want. For instance, inlineable functions are permitted to access non-public properties of a struct; like in C++, there is no way to get at those properties otherwise. You're right that you could introduce another intermediate library, though.

I don't think I trust any client to decide which parts of the library are "safe, probably"

Similarly, I don't trust library authors to decide which parts of their libraries are safe for me to inline in my client.

I think this may be a difference in perspective, e.g. if one works at Apple then one may receive many bug reports of the form "silly app developer, don't use UIKit this way" leading to a general mistrust of clients. Meanwhile, if one has hundreds of filed radars still open (ಠ_ಠ) one might develop a general mistrust of library authors.

To me it seems that this trust problem could be resolved were inlining to be an opt-in feature for each party. The library author should opt-in at the function level, and the client should opt in at least at the library level, and I would prefer at the callsite.

Sorry, my point wasn't "library authors know better than client developers"; it's that trying to make decisions about specific functions isn't sound. It's possible the body of one function makes assumptions based on other (guaranteed, version-independent) knowledge about the library, the simplest form being that that function calls other inlineable functions.

I really don't think we want average app developers to be accessing types like NSRect indirectly, though, and if you have to opt in to accessing NSRect directly, well, everyone will do it all the time, and it'll cease to be meaningful.

Jordan

···

On Feb 11, 2016, at 16:56, Drew Crawford <drew@sealedabstract.com> wrote:

On Feb 11, 2016, at 6:26 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

Do we know if this has happened? This seems like a question that could be resolved by diffing publicly-available headers.

Granted, Cocoa is not an ideal dataset to draw conclusions for my problem domain, but it is better than nothing. If it hasn't occurred in Cocoa, that might pacify me.

···

On Feb 11, 2016, at 6:19 PM, Jordan Rose <jordan_rose@apple.com> wrote:

Well, Apple itself will be using this model, so if there's a bug in an inlineable function in Cocoa, this could make a difference.

Knowing for sure that your client is inlining isn't extra information over knowing it may inline.

(Technically, a C function marked 'inline' and not 'static' isn't guaranteed to be inlined either, but in practice nobody does that because the rules are awful.)

I'm referring specifically to the C/C++ case, where "inlining a dynamic library" means "calling a function (or macro) with a body that appears in a .h file".

In that case I am 100% sure that the function was inlined. I look at the header file and there it is.

An argument was advanced that "we do this already in C/C++" We do *something*, but not *this*.

I really don't think we want average app developers to be accessing types like NSRect indirectly, though, and if you have to opt in to accessing NSRect directly, well, everyone will do it all the time, and it'll cease to be meaningful.

It seems to me that this can be solved at the xcodeproj level. Xcode projects have "default settings" and somebody can ship in that template "--inline=UIKit".

The argument that "we want average app developers to be accessing types like NSRect indirectly" sounds vendor-specific to me, there is a good way here to ship that intuition in a vendor-specific location.

···

On Feb 11, 2016, at 7:32 PM, Jordan Rose <jordan_rose@apple.com> wrote:

A less vendor-specific example would be the Int struct in the standard library.

  David

···

On Feb 11, 2016, at 5:57 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 11, 2016, at 7:32 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

Knowing for sure that your client is inlining isn't extra information over knowing it may inline.

(Technically, a C function marked 'inline' and not 'static' isn't guaranteed to be inlined either, but in practice nobody does that because the rules are awful.)

I'm referring specifically to the C/C++ case, where "inlining a dynamic library" means "calling a function (or macro) with a body that appears in a .h file".

In that case I am 100% sure that the function was inlined. I look at the header file and there it is.

An argument was advanced that "we do this already in C/C++" We do *something*, but not *this*.

I really don't think we want average app developers to be accessing types like NSRect indirectly, though, and if you have to opt in to accessing NSRect directly, well, everyone will do it all the time, and it'll cease to be meaningful.

It seems to me that this can be solved at the xcodeproj level. Xcode projects have "default settings" and somebody can ship in that template "--inline=UIKit".

The argument that "we want average app developers to be accessing types like NSRect indirectly" sounds vendor-specific to me, there is a good way here to ship that intuition in a vendor-specific location.

Knowing for sure that your client is inlining isn't extra information over knowing it may inline.

(Technically, a C function marked 'inline' and not 'static' isn't guaranteed to be inlined either, but in practice nobody does that because the rules are awful.)

I'm referring specifically to the C/C++ case, where "inlining a dynamic library" means "calling a function (or macro) with a body that appears in a .h file".

In that case I am 100% sure that the function was inlined. I look at the header file and there it is.

An argument was advanced that "we do this already in C/C++" We do *something*, but not *this*.

My point is that it's not substantially different. Knowing whether or not the function is definitely inlined doesn't change any action you want to take in response to that. (As you noted, this does require knowing whether the library has any inlineable code.)

I really don't think we want average app developers to be accessing types like NSRect indirectly, though, and if you have to opt in to accessing NSRect directly, well, everyone will do it all the time, and it'll cease to be meaningful.

It seems to me that this can be solved at the xcodeproj level. Xcode projects have "default settings" and somebody can ship in that template "--inline=UIKit".

The argument that "we want average app developers to be accessing types like NSRect indirectly" sounds vendor-specific to me, there is a good way here to ship that intuition in a vendor-specific location.

There's no practical difference to a client between "settings in a library" and "vendor settings shipped with a library", so I guess you're asking for a way to disable inlining code from a particular library? I still think this is something better done on the client side.

You've brought up a good point, though: marking code inlineable needs to be taken very seriously. '@inlineable' and '@fixed_contents' are both placeholder names; they were intended for bikeshedding in the individual proposals to introduce them. Can you come up with something scarier that makes people less likely to use them on a whim? (This is by analogy with things like "UnsafePointer"; "@unsafe_inlineable" isn't quite right but gets at what I mean.)

('@closed' is also a fragility attribute, but not one someone is likely to misuse, and also one that's much less likely to cause a security-level must-update-everything issue. The only case where that would come up is where you can fix the bug with a new value entirely on the library side—as in, existing clients don't need to do anything when they see that new value—which seems unlikely.)

Jordan

···

On Feb 11, 2016, at 17:57, Drew Crawford <drew@sealedabstract.com> wrote:

On Feb 11, 2016, at 7:32 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

I just re-read your initial objection, which had a slightly different idea: allow inlining, but if the library has changed when I go to run it, refuse to launch. I wanted to respond to that explicitly: this is no different from treating the library as part of your own distribution (what the document calls a "resilience domain"), and then not shipping it with your app. That means I see two options:

- Mark a dependency as version-locked; if the library changes in any way, refuse to launch.
- Mark a dependency as non-inlineable.

I think these are both client-side controls, perhaps things you put in your Manifest.swift file. I'm not sure that we actually need or want both of them, but does this formulation seem reasonable to you?

Jordan

P.S. I hope I'm not coming off as antagonistic in these emails, particularly with the repeated responses. I think you've brought up a very important issue and it's critical that the ramifications of the model, whatever we decide on, are well-understood and can be meaningfully acted upon.