⚠️ Important packages are being released using unstable language features

As pointed out in the pitch thread, a number of libraries are already making use of the _modify accessor:

This thread is to discuss how we should deal with that. The problem is that if you use unstable language features, your library and its releases are by definition not stable:

One idea might be to use the compiler's support for optional language features. For example, the following code compiles:

#if compiler(>=5.3) && $StaticAssert

(Example from the test suite. The list of currently-supported features can be found here).

We could add a feature, like $ModifyAccessorV1, so that these libraries could conditionally support _modify accessors with compatible compilers, and issue a warning while falling back to regular getters and setters if _modify ever gets dropped or substantially revised.

This wouldn't solve the problem of existing releases being unstable, nor the problem of adoption by ABI-stable libraries, but it would allow future releases of source packages to degrade gracefully, while also allowing third-party packages to safely experiment with the feature.


As @Ben_Cohen said,

... there is no ambiguity here. Underscored attributes are not source stable and that means you need to deal with the consequences of them not being source stable, whether you're a leaf target or a package.

I would assume that the assumption that _modify is a source-stable feature constitutes programmer error, regardless of how "canon" one's package is to the Swift ecosystem. I like swift-nio's shim: a function meant to emulate the behavior of the _modify accessor in a way that meets the library's needs without resorting to unstable underscored keywords.

If a library author must have the behavior of _modify as it was in a particular version of Swift, that's what #if is for, and should suffice as long as that version of Swift is available to compile against. Once _modify is removed in some newer version of Swift, library authors should transition to the new syntax that then-modern Swift will provide.

I may be missing something, but I don't see this issue as any different from the many syntax transitions that Swift has undergone at every major milestone in its development.

Oh, and a few more examples just occurred to me:

Also, async-http-client uses @inline(__always), but I'm not sure if that counts as first-party.

Now, I'm not blaming the authors of these packages - it is clear that this is a widespread problem, showing a general lack of discipline across the community when it comes to publishing releases making use of unstable language features. But I think developers do and should expect better, especially from first-party packages.

Just to reiterate @Ben_Cohen's point:

If a package, distributed in source form, makes use of underscored keywords or attributes, it is not guaranteed to compile with the next patch/minor/major release of the compiler. The entire package, and any projects which depend on them, become compromised and are subject to the same lack of stability guarantees.

This is a significant danger lurking in the package ecosystem; it undercuts the idea that Swift is source-stable if so many important packages are publishing releases using unstable language features.

Also, it's just poor form. Why are some packages allowed to use these features and claim to be stable, but my packages cannot? They are able to offer better performance, modularity, and nicer APIs than is possible using actual, stable Swift language features.

This sounds to me like an undeserved blame. People work with the tools that are available. I would not look at people when the tools are lacking. :eyes:

EDIT: my own sentence may sound like a blame as well. Apologies. I just really wish we'd talk about problem people have, instead of things people do in an incorrect way.


The goal here is not to blame anybody, but to accept that:

  1. This is a problem
  2. We need to fix it before it gets worse
  3. We need to prevent it happening again

The obvious solution to (2) is to work to get these features through evolution, so they can become official, supported parts of the language. Solving (3) is perhaps a bit trickier, but could involve making better use of the feature-test abilities present in more recent compilers, as mentioned in the OP. That way, packages could continue to compile, perhaps with degraded performance, if these unstable features ever disappeared or were significantly changed.


Regarding ③, for unstable/experimental features, Kotlin has the concept of @RequiresOptIn which force the user of annotated API to pass compiler flags to enable them.

In our case, forcing usage of compiler flag would at least force package maintainer to distribute their packages using the SwiftSetting.unsafeFlag directive, which will prevent accidental release of source using unsupported feature.

1 Like

The _enclosingInstance subscript variant for a property wrapper comes to mind too.

I think the problem is Swift keeps adding interesting features without standardizing them. Then Apple (generally) is quick to adopt those non-standard features in their own frameworks, which creates pressure for others to do the same. People write articles to explain how they work (like this one where I found a mention of _enclosingInstance) and various people start using it.

I don't think an underscore means much to most people. A different compiler flag to enable each feature would be more effective, but in the end if there's demand people will adopt the feature despite the nuisances you throw at them.

Once the feature is present in the compiler, it reduces the pressure for standardizing it because those who needed it will now use it despite the underscore. And we end up in this limbo world of non-standard features accumulating and no one having a self-interest in doing the work to standardize them.

Terms of Service

Privacy Policy

Cookie Policy