Default swift language version for a given toolchain version

To follow up from Pitch: [SwiftPM] Swift Language Version Per Target

From original thread:

To break out the discussion about what the default language version should be for a given toolchain version to a new thread;

I’d argue for matching the toolchain and language versions by default with escape hatches as outlined in the related thread for migration.

6 Likes

Keeping the default language mode at -swift-version 5 is the only choice that doesn't compromise source compatibility. There is too much code out there that doesn't specify any language version, which means it's building with -swift-version 5. That code cannot break just by upgrading to Swift 6.0 tools. I believe the fact that the Swift 5.0 compiler changed the default language mode is part of the reason why much of the community has been worried about the Swift 6.0 compiler breaking their code without them having done anything to opt into complete concurrency checking. The Swift 6 migration is more involved than previous migrations, and it will not be forced on anybody who does not want to migrate.

It depends on what is meant by "Swift 6 features". You will need to pass -swift-version 6 to enable the source-incompatible upcoming features that are enabled by default in the Swift 6 language mode, which is primarily complete concurrency checking. Other features, like typed throws, can be used without passing -swift-version 6.

I think there are other ways to encourage code to be written in the Swift 6 language mode as the Swift ecosystem grows, such as changing any templates, e.g. the package template created with swift package init, to explicitly specify -swift-version 6 from the start. I think the best path for getting the community to migrate to Swift 6 is for them to want to migrate, not for them to download a new tool set and have their existing code break, leading them to believe they need to fix the errors instead of fixing their build setup to explicitly specify -swift-version 5.

It might be possible to move to a world where all code using prior language versions has to explicitly specify it. For that, we would probably need some kind of file-level directive for specifying language versions in a primary file, e.g. main.swift.

2 Likes

I think it’s worth being precise about what “upgrading to 6.0 tools” means. Switching to Xcode 16 or whatever wouldn’t break anything, because all Package.swift files start with a version number. (And I agree that the interpretation of Package.swift files without a version number shouldn’t change.)

So we’re talking about someone who has edited their Package.swift to say “6.0” at the top. Why can’t that have breaking changes and a little migration guide?

10 Likes

I agree with @jrose here. If I manually change the tools version in my package manifest to 6.0 I expect the default language to also change.

8 Likes

I think it's also important to note that in the context of SwiftPM package and Xcode projects, explicit
-swift-version flags are already typically passed to the compiler.

I think this flag is always passed for SwiftPM packages.

Xcode project templates currently automatically define a SWIFT_VERSION build setting of 5 which is passed to the compiler. Xcode builds fail if no SWIFT_VERSION is set. (Thank you @NeoNacho Default swift language version for a given toolchain version - #9 by NeoNacho)

So, in many existing developer contexts, the compiler default language mode is already overridden.

I think this ends up being three different decisions about language version/mode:

  • How SwiftPM chooses the default language mode it passes to the compiler
  • The language mode set in Xcode templates
  • The compiler default language mode

SwiftPM

For SwiftPM, I believe that SwiftPM should pass the -swift-version 6 flag by default once a package has been updated to declare // swift-tools-version:6.0.

It is an explicit opt-in for existing packages so swift-tools-version 5.x packages continue to use version 5 by default. (Pitch: [SwiftPM] Swift Language Version Per Target - #12 by James_Dempsey)

Xcode

For Xcode, since it's proprietary, as a community, we don't really have insight into when templates might change to use SWIFT_VERSION 6.

But even then, most existing Xcode projects will continue building with SWIFT_VERSION 5 since that has been part of templates for a long while now.

I could also imagine Xcode checking if a project does not have a SWIFT_VERSION build setting and notifying the user and letting them choose between 5 and 6.

One thing I do hope Apple-internal folks guard against is encouraging SWIFT_VERSION 6 as one of the 'recommended build setting updates' that appear when opening a project with a new Xcode version.

Compiler Default

I don't have any statistics as to how much Swift is compiled via SwiftPM commands vs xcodebuild of Xcode projects vs 'bare' use of calling the compiler using things like makefiles.

But, I would imagine a great deal of compilation is in the context of building SwiftPM packages and Xcode projects, which override the compiler default.

I could see the built-in default going either way.

UPDATE: Updated with information that Xcode builds fail if SWIFT_VERSION build setting is not present.

3 Likes

Yes, sorry! I specifically mean a change that causes you to use a Swift 6.0 compiler, such as downloading and trying out a 6.0 nightly toolchain with an existing project, and not making any source changes. I'm okay with the default language mode that SwiftPM passes to change when you bump your package manifest to require 6.0 tools. I'm not okay with changing the default language mode in the Swift 6.0 compiler itself. The default in the compiler impacts all kinds of projects, not just packages.

3 Likes

Just to clarify because I think everyone is actually on the same page here:

  • Take an existing package with the tools version set to 5.10, for example and build it with the Swift 6 compiler - Swift 5 language mode
  • New package/updated package with the tools version set to 6.0 and build it with the Swift 6 compiler - Swift 6 language mode (and swift build will use the Swift 6 language mode for building a package)

That's the current aim correct?

The only clarification I think is if I have a package that has specified the tools version as 6.0 but it pulls in dependencies that still use 5.10 - the 6.0 target will use the Swift 6 language mode but the dependencies will still be built with the Swift 5 language mode

6 Likes

They actually cannot do that since if they do, they'll get a build error:

Value for SWIFT_VERSION cannot be empty.

This has been the case for a long time, at least since the introduction of the new build system.

1 Like

Thank you, I'll update my original post to reflect this.

1 Like

Then I think we are on the same page too, I got the impression that it wouldn’t be enough to just edit the tools version in a package manifest to get the new language mode, which would have been a bit unexpected.

I can understand the pragmatism of not wanting to change the default version when using the compiler directly, but that’ll mean then that version 5 is basically forever the default? Swift toolchain version 10 would then still default to swift language version 5 with the same argument - to not break any old code out there.that will over time be stranger and stranger…

Perhaps should start issuing a warning if the language version isn’t specifically specified from the command line? Or as a fix-it if trying to use new features (which might be hard?). Just seems like a nice experience for someone who wants to build a single file to test the language out would get the version of the language that is current and not a “random” version from years ago which might not then compile?

6 Likes

Or maybe even add an environment sledgehammer setting SWIFT_LANGUAGE_VERSION which would be picked up, the we could change the toolchain version and for any code base requiring swift 5 language mode it can be set in the environment.

I don't think there's a need to introduce yet another environment variable to handle a build setting.

In general, environment variables are stringly typed, hard to manage, and hard to discover.

If you need a sledgehammer, you can build with swift build -Xswiftc -swift-version -Xswiftc 6, which will apply the language version setting to parts of the build graph configured for a target triple. And at least you'll see an error message if you make a typo, unlike with environment variables.

2 Likes

More than fair enough. The at least start warning that swift language mode should be explicitly passed so we can at least start pushing against having a toolchain version and language version get in sync in the future. It just rubs be the wrong way to have the swift toolchain version 32 ship with language version 5 :slight_smile:

1 Like

I just ran into this versioning issue, got some questions about this. I was aware that this decision was announced earlier this year, but was surprised by the concrete implications recently:

> cat check.swift
#if swift(>=6.0)
print("got to 6.0")
#else
print("no go to 6.0")
#endif
> ./swift-DEVELOPMENT-SNAPSHOT-2024-07-29-a-ubi9/usr/bin/swiftc check.swift  && ./check
no go to 6.0
> ./swift-6.0-DEVELOPMENT-SNAPSHOT-2024-08-02-a-ubi9/usr/bin/swiftc check.swift  && ./check
no go to 6.0

> ./swift-DEVELOPMENT-SNAPSHOT-2024-07-29-a-ubi9/usr/bin/swiftc check.swift -swift-version 6 && ./check
got to 6.0

I understand that most Swift code out there now expects language mode 5 and language mode 6 introduces breaking changes, but it is quite weird that the upcoming 6.0 and trunk 6.1 compilers now default to -swift-version 5 internally.

Has the converse default been considered, ie the 6.0 compiler defaults to language mode 6 internally but Xcode and SwiftPM pass in -swift-version 5 to the compiler by default and override it? That would only leave external build systems like CMake or Bazel to decide if they also want to pass in -swift-version 5 by default for the Swift 6 toolchain.

If that's not going to work, what is the timeline with keeping language mode 5 the default for future compilers: indefinitely or is it the hope to switch the compiler default to language mode 6 sometime in the next year or two?

I already see projects switching to wrongly use #if compiler(>=6) to gate Swift 6 language features and work around this problem.

3 Likes

I also faced with it yesterday and very confused by this behavior.