- Proposal: SE-NNNN
- Author: James Dempsey
- Review Manager: TBD
- Status: Awaiting implementation
- Implementation: apple/swift-package-manager#7620
- Review: (first pitch), (second pitch)
The term "Swift version” can refer to either the toolchain/compiler version or the language mode. This ambiguity is a consistent source of confusion. This proposal formalizes the term language mode in tool options and APIs.
The proposed solution is to use the term language mode for the appropriate Swift compiler option, Swift Package Manager APIs, and for a new languageMode()
compilation condition. Use of "Swift version" to refer to language mode will be deprecated or obsoleted as needed.
The term language mode has been consistently used to describe this compiler feature since it was introduced with Swift 4.0 and is an established term of art in the Swift community.
The Alternatives Considered section contains a more detailed discussion of the term's history and usage.
Introduce a -language-mode
option that has the same behavior as the existing -swift-version
option, while de-emphasizing the -swift-version
option in help and documentation.
Introduce a new languageMode(…)
compiler condition with the same syntax as the existing compiler()
and swift()
conditions, but which only accepts the defined set of language mode values.
#if languageMode(<6)
...
#endif
For the proposed compiler and language changes the term 'language mode' is used instead of 'Swift language mode' because the context of their usage strongly implies a Swift language mode.
Introduce four Swift Package Manager API changes limited to manifests >= 6.0:
@available(_PackageDescription, introduced: 6)
Package(
name: String,
defaultLocalization: [LanguageTag]? = nil.
platforms: [SupportedPlatform]? = nil,
products: [Product] = [],
dependencies: [Package.Dependency] = [],
targets: [Target] = [],
swiftLanguageModes: [SwiftLanguageMode]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
)
Add a new init
method to Package
with the following changes from the current init
method:
- The paramater
swiftLanguageVersions
is renamed toswiftLanguageModes
- The parameter type is now an optional array of
SwiftLanguageMode
values instead ofSwiftVersion
values
The existing init method will be marked as obsolete and renamed allowing the compiler to provide a fix-it.
Rename the public Package
property swiftLanguageVersions
to swiftLanguageModes
. Add a swiftLanguageVersions
computed property that accesses swiftLanguageModes
for backwards compatibility.
Rename the SwiftVersion
enum to SwiftLanguageMode
. Add SwiftVersion
as a type alias for backwards compatibility.
4. Update API accepted in SE-0435: Swift Language Version Per Target:
public struct SwiftSetting {
// ... other settings
@available(_PackageDescription, introduced: 6.0)
public static func swiftLanguageMode(
_ mode: SwiftLanguageMode,
_ condition: BuildSettingCondition? = nil
)
If both proposals are implemented in the same release, the accepted SE-0435 API would be added with the proposed naming change.
In Swift PM manifests, multiple languages are supported. For clarity, there is existing precedent for parameter and enum type names to have a language name prefix.
For example the Package init
method currently includes:
...
swiftLanguageVersions: [SwiftVersion]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
...
For clarity and to follow the existing precedent, the proposed Swift PM APIs will be appropriately capitalized versions of "Swift language mode".
A new -language-mode
option will be added with the same behavior as the existing -swift-version
option.
The -swift-version
option will continue to work as it currently does, preserving backwards compatibility.
The -language-mode
option will be presented in the compiler help.
The -swift-version
option will likely be supressed from the top-level help of the compiler. More investigation is needed on the details of this.
The new languageMode(…)
compiler condition uses the same syntax of the existing compiler(…)
and swift(…)
conditions, allowing for >=
and <
.
#if languageMode(>=6)
...
#endif
#if languageMode(<4.2)
...
#endif
The new compiler condition will only accept the defined set of language mode values. The compiler will generate an error if provided a value that is not a valid language mode. This mirrors the behavior of the existing compiler option.
The diagnostic message will be similar to the existing command-line error message:
#if languageMode(>=5.2) // Error: Invalid value '5.2', valid arguments are '4', '4.2', '5'
...
#endif
An exception to this behavior are for language modes that were supported by previous compiler versions but are no longer supported. To maintain source compatibility, these will generate a warning, not an error. Once-supported language modes are assumed to be older than any currently supported language mode. When used with >=
the code block will always compile. When used with <
the code block will never compile.
#if languageMode(>=3) // Warning: Unsupported language mode '3', valid arguments are '4', '4.2', '5'
... // This code will always compile
#endif
#if languageMode(<3) // Warning: Unsupported language mode '3', valid arguments are '4', '4.2', '5'
... // This code will never compile
#endif
Note that this is meant only for source compatibiity going forward. The initial implementation is only required to support the set of language modes supported by the compiler when this proposal is implemented.
No changes are proposed for the existing swift()
compilation condition. It will remain as-is.
Proposed Swift Package Manager API changes are limited to manifests >= 6.0:
A new init
method will be added to Package
that renames the swiftLanguageVersions
parameter to swiftLanguageModes
with the type of the parameter being an optional array of SwiftLanguageMode
values instead of SwiftVersion
values:
@available(_PackageDescription, introduced: 6)
Package(
name: String,
defaultLocalization: [LanguageTag]? = nil.
platforms: [SupportedPlatform]? = nil,
products: [Product] = [],
dependencies: [Package.Dependency] = [],
targets: [Target] = [],
swiftLanguageModes: [SwiftLanguageMode]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
)
The existing init method will be marked as obsoleted and renamed, allowing the compiler to provide a fix-it:
@available(_PackageDescription, introduced: 5.3, obsoleted: 6, renamed:
"init(name:defaultLocalization:platforms:pkgConfig:providers:products:
dependencies:targets:swiftLanguageModes:cLanguageStandard:
cxxLanguageStandard:)")
public init(
name: String,
...
swiftLanguageVersions: [SwiftVersion]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
) {
The existing method must be obsoleted because the two methods are ambiguous when the default value for swiftLanguageVersions
/ swiftLanguageModes
is used:
Package ( // Error: Ambiguous use of 'init'
name: "MyPackage",
products: ...,
targets: ...
)
This approach follows the pattern of past revisions of the Package init
method.
See the Source compatibiity section for more details about this change.
Rename the Package
public property swiftLanguageVersions
property to swiftLanguageModes
. Introduce a computed property named swiftLanguageModes
that accesses the renamed stored property for backwards compatibility.
The computed property will be annotated as obsoleted in Swift 6 and renamed to swiftLanguageModes
.
For packages with swift tools version less than 6.0, accessing the swiftLanguageModes
property will continue to work.
For 6.0 and later, that access will be an error with a fix-it to use the new property name.
@available(_PackageDescription, obsoleted: 6, renamed: "swiftLanguageModes")
public var swiftLanguageVersions: [SwiftVersion]? {
get { swiftLanguageModes }
set { swiftLanguageModes = newValue }
}
See the Source compatibiity section for more details about this change.
Rename the existing SwiftVersion
enum to SwiftLanguageMode
with SwiftVersion
added back as a type alias for backwards compatibility.
This change will not affect serializaiton of PackageDescription types. Serialization is handled by converting PackageDescription types into a mirrored set of Codable types. The existing serialization types will remain as-is.
The API in the newly-accepted SE-0435 will change to use the language mode terminology:
public struct SwiftSetting {
// ... other settings
@available(_PackageDescription, introduced: 6.0)
public static func swiftLanguageMode(
_ mode: SwiftLanguageMode,
_ condition: BuildSettingCondition? = nil
)
The name of the function is swiftLanguageMode()
instead of languageMode()
to keep naming consistent with the swiftLanguageModes
parameter of the Package init method. The parameter label mode
is used to follow the precedent set by interoperabilityMode()
in SwiftSetting
.
The new Package init
method and obsoleting of the existing init
method will cause source breakage for package manifests that specify the existing swiftLanguageVersions
parameter when updating to swift tools version 6.0
A search of manifest files in public repositories suggests that about 10% of manifest files will encounter this breakage.
Because the obsoleted init
method is annotated as renamed
the compiler will automatically provide a fix-it to update to the new init
method.
Renaming the public swiftLanguageVersions
property of Package
preserves backwards compatibility by introducing a computed property with that name. Because this proposal already contains a necessary breaking change as detailed above, the computed property will also be marked as obsoleted in 6.0 and annotated as renamed
to provide a fix-it.
Searching manifest files suggests that accessing the swiftLanguageVersions
property directly is not common. Making both breaking changes at once results in applying at most two fix-its to a manifest file instead of one.
This proposal has no effect on ABI stability.
In the pitch phase, a number of terms were suggested as alternatives for language mode. Some concerns were also expressed that the term language mode may be too broad and cause future ambiguity.
The intent of this proposal is to formalize terminology already in use in tool options and APIs.
The term language mode is a long-established term of art in the Swift community to describe this functionality in the compiler.
This includes the blog post annoucing the functionality as part of the release of Swift 4 in 2017 (emphasis added):
With Swift 4, you may not need to modify your code to use the new version of the compiler. The compiler supports two language modes…
The language mode is specified to the compiler by the -swift-version flag, which is automatically handled by the Swift Package Manager and Xcode.
One advantage of these language modes is that you can start using the new Swift 4 compiler and migrate fully to Swift 4 at your own pace, taking advantage of new Swift 4 features, one module at a time.
Usage also includes posts in the last year from LSG members about Swift 6 language mode:
Finally, searching of "language modes" and "language mode" in the Swift forums found that at least 90% of the posts use the term in this way. Many of the remaining posts use the term in the context of Clang.
Alternate terms raised as possibilities were:
- Edition: a term used by Rust for a similar concept
- Standard: similar to C or C++ standards
Language standards tend to be associated with a written specification, which Swift does not currently have.
Using the term standard would preclude using the term with its more widespread meaning.
Some reviewers raised concern that Embedded Swift could be considered a language mode and lead to future ambiguity.
On consideration, this concern is mitigated by two things:
-
As noted above, the term language mode is a well-established term of art in the Swift community.
-
The term Embedded Swift already provides an unambiguous, concise name that can be discussed without requiring a reference to modes.
This is demonstrated by the following hypothetical FAQ based on the Embedded Swift vision document:
What is Embedded Swift?
Embedded Swift is a subset of Swift suitable for restricted environments such as embedded and low-level environments.How do you enable Embedded Swift?
Pass the-embedded
compiler flag to compile Embedded Swift.
Considering these alternatives, it seems likely that introducing a new term to replace the long-established term language mode and potentially giving the existing term a new meaning would lead to more ambiguity than keeping and formalizing the existing meaning of language mode.