Last active
June 14, 2026 02:03
-
-
Save zadr/7d420f936f89e9c62b0312ca3f18a3a4 to your computer and use it in GitHub Desktop.
Lists the enableUpcomingFeature/enableExperimentalFeature flags your Swift toolchain accepts (parsed from its own Features.def), marks which are redundant in your language mode, and diffs against past releases to show what's new.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env swift | |
| // | |
| // list-swift-features.swift | |
| // | |
| // Lists the feature names accepted by `Package.swift`'s | |
| // .enableUpcomingFeature("…") and .enableExperimentalFeature("…") | |
| // for the *active* Swift toolchain, annotated against your effective | |
| // language mode (upcoming flags that are already default-on are redundant). | |
| // | |
| // How it works — no C-grammar parsing: | |
| // The compiler defines these in `include/swift/Basic/Features.def`, an | |
| // X-macro include (not standalone C). We download the copy from the GitHub | |
| // release branch matching `swift --version`, then compile a tiny C driver | |
| // that supplies its own macro definitions and `#include`s the .def. The C | |
| // preprocessor expands each entry through the macro that defined it, so | |
| // classification is exact — we print the buckets we care about and let | |
| // everything else collapse to a no-op. | |
| // | |
| // Usage: | |
| // swift list-swift-features.swift # all groups | |
| // swift list-swift-features.swift upcoming # one group | |
| // swift list-swift-features.swift --mode 6 # judge upcoming flags against mode 6 | |
| // swift list-swift-features.swift --branch main # override toolchain branch | |
| // swift list-swift-features.swift --new # mark entries added since the previous release | |
| // swift list-swift-features.swift --since 6.1 # mark entries added since release/6.1 (implies --new)// | |
| // | |
| import Foundation | |
| // MARK: - Diagnostics | |
| func warn(_ message: String) { FileHandle.standardError.write(Data("warning: \(message)\n".utf8)) } | |
| func die(_ message: String) -> Never { | |
| FileHandle.standardError.write(Data("error: \(message)\n".utf8)) | |
| exit(1) | |
| } | |
| // MARK: - Subprocess (irreducibly imperative: Process owns mutable pipes) | |
| @discardableResult | |
| func run(_ launchPath: String, _ args: [String]) -> (status: Int32, out: String, err: String) { | |
| let p = Process() | |
| p.executableURL = URL(fileURLWithPath: launchPath) | |
| p.arguments = args | |
| let outPipe = Pipe(), errPipe = Pipe() | |
| p.standardOutput = outPipe | |
| p.standardError = errPipe | |
| do { try p.run() } catch { return (-1, "", "failed to launch \(launchPath): \(error)") } | |
| // Drain before waitUntilExit to avoid a pipe-buffer deadlock on large output. | |
| let out = outPipe.fileHandleForReading.readDataToEndOfFile() | |
| let err = errPipe.fileHandleForReading.readDataToEndOfFile() | |
| p.waitUntilExit() | |
| return (p.terminationStatus, String(decoding: out, as: UTF8.self), String(decoding: err, as: UTF8.self)) | |
| } | |
| /// Resolve a developer tool (cc, swiftc, …) through `xcrun --find`. | |
| func xcrunFind(_ tool: String) -> String? { | |
| let r = run("/usr/bin/xcrun", ["--find", tool]) | |
| let path = r.out.trimmingCharacters(in: .whitespacesAndNewlines) | |
| return (r.status == 0 && !path.isEmpty) ? path : nil | |
| } | |
| /// First capture group of `pattern` in `text`, or nil. | |
| func firstMatch(_ pattern: String, in text: String) -> String? { | |
| guard let re = try? NSRegularExpression(pattern: pattern), | |
| let m = re.firstMatch(in: text, range: NSRange(text.startIndex..., in: text)), | |
| m.numberOfRanges > 1, | |
| let r = Range(m.range(at: 1), in: text) | |
| else { return nil } | |
| return String(text[r]) | |
| } | |
| // MARK: - Options | |
| struct Options { | |
| var groups: Set<String> = ["upcoming", "experimental", "optional"] | |
| var branch: String? | |
| var mode: Int? | |
| var markNew = false // diff against a baseline; mark/section entries new vs it | |
| var since: String? // explicit baseline version "x.y" (else: previous release) | |
| } | |
| func parseOptions(_ argv: [String]) -> Options { | |
| var opts = Options() | |
| var explicitGroups: Set<String> = [] | |
| var it = argv.makeIterator() | |
| while let arg = it.next() { | |
| switch arg { | |
| case "--branch": | |
| guard let b = it.next() else { die("--branch needs a value") } | |
| opts.branch = b | |
| case "--mode": | |
| guard let m = it.next(), let n = Int(m) else { die("--mode needs an integer (e.g. 6)") } | |
| opts.mode = n | |
| case "--new": | |
| opts.markNew = true | |
| case "--since": | |
| guard let v = it.next() else { die("--since needs a version (e.g. 6.1)") } | |
| guard firstMatch(#"^(\d+\.\d+)$"#, in: v) != nil else { | |
| die("--since expects a version like 6.1, got '\(v)'") | |
| } | |
| opts.since = v | |
| opts.markNew = true // --since implies a diff | |
| case "-h", "--help": | |
| print(""" | |
| usage: swift list-swift-features.swift [group] [--mode N] [--branch <name>] [--new] [--since x.y] | |
| group: upcoming | experimental | optional (default: all) | |
| --mode N: language-mode major to judge upcoming flags against | |
| (default: derived from ./Package.swift tools-version) | |
| --branch: override the Features.def source branch (e.g. main) | |
| --new: diff against the previous release branch; mark added entries | |
| --since x.y: diff against release/x.y instead of the previous release | |
| (implies --new) | |
| Upcoming features are annotated relative to the effective language | |
| mode: a feature default-on in a mode <= yours is 'redundant' (the | |
| flag is a no-op); one bound to a higher mode is still 'opt-in'. | |
| """) | |
| exit(0) | |
| case "upcoming", "experimental", "optional": | |
| explicitGroups.insert(arg) | |
| default: | |
| die("unknown argument: \(arg)") | |
| } | |
| } | |
| if !explicitGroups.isEmpty { opts.groups = explicitGroups } | |
| return opts | |
| } | |
| // MARK: - Toolchain → release branch | |
| /// "Apple Swift version 6.3 …" → "release/6.3"; nil if unparseable. | |
| func branchForActiveToolchain() -> String? { | |
| guard let swiftc = xcrunFind("swift") ?? xcrunFind("swiftc") else { | |
| die("could not locate the swift toolchain via xcrun") | |
| } | |
| let v = run(swiftc, ["--version"]) | |
| return firstMatch(#"Swift version (\d+\.\d+)"#, in: v.out + v.err).map { "release/\($0)" } | |
| } | |
| /// Parse an "X.Y" version string into a comparable (major, minor) tuple. | |
| func versionTuple(_ s: String) -> (Int, Int) { | |
| let p = s.split(separator: ".").compactMap { Int($0) } | |
| return (p.first ?? 0, p.count > 1 ? p[1] : 0) | |
| } | |
| /// Order two "X.Y" version strings. | |
| func compareVersions(_ a: String, _ b: String) -> ComparisonResult { | |
| let (ta, tb) = (versionTuple(a), versionTuple(b)) | |
| if ta < tb { return .orderedAscending } | |
| if ta > tb { return .orderedDescending } | |
| return .orderedSame | |
| } | |
| /// The "X.Y" of a release/X.Y branch, or nil for non-release branches (e.g. main). | |
| func releaseVersion(of branch: String) -> String? { | |
| firstMatch(#"^release/(\d+\.\d+)$"#, in: branch) | |
| } | |
| /// The release branch immediately preceding `branch` (e.g. release/6.3 → | |
| /// release/6.2), found by version-sorting the remote's release/* heads. nil if | |
| /// `branch` isn't a release/X.Y or has no predecessor. Used by --new. | |
| func previousReleaseBranch(before branch: String) -> String? { | |
| guard let cur = releaseVersion(of: branch) else { return nil } | |
| guard let git = xcrunFind("git") ?? "/usr/bin/git" as String? else { return nil } | |
| let ls = run(git, ["ls-remote", "--heads", | |
| "https://github.com/swiftlang/swift.git", "refs/heads/release/*"]) | |
| guard ls.status == 0 else { return nil } | |
| // Parse "X.Y" versions, sort numerically by (major, minor), find the one before cur. | |
| let versions = ls.out.split(separator: "\n").compactMap { | |
| firstMatch(#"refs/heads/release/(\d+\.\d+)$"#, in: String($0)) | |
| } | |
| let sorted = versions.sorted { versionTuple($0) < versionTuple($1) } | |
| guard let i = sorted.firstIndex(of: cur), i > 0 else { return nil } | |
| return "release/\(sorted[i - 1])" | |
| } | |
| // MARK: - Fetch Features.def | |
| /// Raw fetch of one branch's Features.def, or nil if it 404s / is empty. | |
| func getFeaturesDef(_ branch: String) async -> String? { | |
| let url = URL(string: "https://raw.githubusercontent.com/swiftlang/swift/\(branch)/include/swift/Basic/Features.def")! | |
| guard let (data, resp) = try? await URLSession.shared.data(from: url), | |
| (resp as? HTTPURLResponse)?.statusCode == 200, !data.isEmpty | |
| else { return nil } | |
| return String(decoding: data, as: UTF8.self) | |
| } | |
| /// Fetch the primary branch's Features.def, falling back to `main` if the | |
| /// requested branch is missing (the toolchain branch may lag the repo). | |
| func fetchFeaturesDef(branch: String) async -> (text: String, branch: String) { | |
| if let text = await getFeaturesDef(branch) { return (text, branch) } | |
| if branch != "main", let text = await getFeaturesDef("main") { | |
| warn("branch '\(branch)' not found; fell back to 'main'") | |
| return (text, "main") | |
| } | |
| die("could not download Features.def for branch '\(branch)'") | |
| } | |
| /// Fetch the baseline Features.def to diff against (for --new / --since). | |
| /// `since` ("x.y") pins the baseline to release/x.y; otherwise it auto-detects | |
| /// the release branch immediately before `branch`. Returns nil when no baseline | |
| /// applies. Unlike the primary fetch, this does NOT fall back to main: an | |
| /// explicit --since baseline that doesn't exist is a user error, so we die. | |
| /// Wrapped so it binds with `async let` directly (which requires a plain async | |
| /// call, not an await inside a closure). | |
| func fetchBaselineDef(before branch: String, since: String?) async -> (text: String, branch: String)? { | |
| let baseline: String? | |
| if let since { | |
| // Compare an explicit --since against the current branch (when current | |
| // is `main`, every release is older, so any --since is fine): | |
| // newer → error; equal → no-op (nil, like not diffing); older → diff. | |
| if let cur = releaseVersion(of: branch) { | |
| switch compareVersions(since, cur) { | |
| case .orderedDescending: | |
| die("--since \(since) is newer than the current branch (release/\(cur))") | |
| case .orderedSame: | |
| return nil // diffing a version against itself yields nothing | |
| case .orderedAscending: | |
| break | |
| } | |
| } | |
| baseline = "release/\(since)" | |
| } else { | |
| baseline = previousReleaseBranch(before: branch) | |
| } | |
| guard let baseline else { return nil } | |
| guard let text = await getFeaturesDef(baseline) else { | |
| die("baseline '\(baseline)' not found — no Features.def at that branch") | |
| } | |
| return (text, baseline) | |
| } | |
| // MARK: - Feature model | |
| struct Feature { | |
| let group: String // "upcoming" | "experimental" | "optional" | |
| let name: String | |
| let extra: String // upcoming/optional: SE number; experimental: AvailableInProd | |
| let mode: String // upcoming: language-mode major it becomes default-on in; else "" | |
| var isNew = false // absent from the previous release branch (--new) | |
| /// Identity for cross-branch diffing: a feature is "the same" across | |
| /// branches when its group and name match. | |
| var key: String { "\(group)/\(name)" } | |
| /// "SE-123" or "". Only upcoming/optional carry an SE number in `extra`; | |
| /// experimental's `extra` is the AvailableInProd bool, so it has none. | |
| var seLabel: String { | |
| guard group != "experimental", | |
| !extra.trimmingCharacters(in: CharacterSet(charactersIn: "0")).isEmpty | |
| else { return "" } | |
| return "SE-\(extra)" | |
| } | |
| /// The sub-header bucket this row sorts into, given the effective mode. | |
| /// `rank` orders buckets; `label` is the printed sub-header ("" = none). | |
| func bucket(effectiveMode: Int?) -> (rank: Int, label: String) { | |
| switch group { | |
| case "upcoming": | |
| guard let m = Int(mode) else { return (.max, "mode: unspecified") } | |
| guard let eff = effectiveMode else { return (m, "default in mode \(m)") } | |
| return eff >= m | |
| ? (m, "redundant — already default in mode \(m) (flag is a no-op)") | |
| : (1000 + m, "opt-in — default in mode \(m), required until then") | |
| case "experimental": | |
| return extra == "true" | |
| ? (0, "available on release toolchains") | |
| : (1, "asserts/development toolchains only") | |
| default: | |
| return (0, "") | |
| } | |
| } | |
| /// Row-specific tail printed after the name, e.g. " (SE-481; new)". | |
| /// `includeNew` is false when a "new"/"existing" sub-section already says it. | |
| func annotation(includeNew: Bool = true) -> String { | |
| let bits = [seLabel, (includeNew && isNew) ? "new" : ""].filter { !$0.isEmpty } | |
| return bits.isEmpty ? "" : " (\(bits.joined(separator: "; ")))" | |
| } | |
| } | |
| // MARK: - Build the X-macro C driver, compile, run, parse | |
| func extractFeatures(featuresDef: String) -> [Feature] { | |
| // Unique per call: extractFeatures runs more than once per process (e.g. | |
| // --new fetches current + previous), so a PID-only dir would collide and | |
| // a stale driver/output could be reused. | |
| let tmp = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("swiftfeat-\(UUID().uuidString)") | |
| try? FileManager.default.createDirectory(at: tmp, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: tmp) } | |
| let defPath = tmp.appendingPathComponent("Features.def") | |
| let cPath = tmp.appendingPathComponent("driver.c") | |
| let binPath = tmp.appendingPathComponent("driver") | |
| // The .def's own header has #ifndef fallbacks that route every macro form | |
| // (BASELINE_*, MIGRATABLE_*, SUPPRESSIBLE_*, the *_EXCLUDED_* variant, …) | |
| // through the three we define below, so we only classify those three: | |
| // UPCOMING_FEATURE(Name, SENumber, LanguageMode) | |
| // EXPERIMENTAL_FEATURE(Name, AvailableInProd) | |
| // OPTIONAL_LANGUAGE_FEATURE(Name, SENumber, Description) | |
| // Everything else collapses into LANGUAGE_FEATURE → no-op. | |
| // SE numbers and the mode are *stringized* (#x) so a leading-zero token | |
| // like 0337 isn't silently reinterpreted as octal by %d. | |
| // Each macro takes a trailing `...` so arity drift across versions is | |
| // tolerated: e.g. Swift 5.10's LANGUAGE_FEATURE had a 4th arg, which would | |
| // otherwise blow up a fixed-arity definition when --since reaches back that far. | |
| let driverC = """ | |
| #include <stdio.h> | |
| #define LANGUAGE_FEATURE(...) | |
| #define UPCOMING_FEATURE(Name, SE, Mode, ...) \\ | |
| printf("upcoming\\t%s\\t%s\\t%s\\n", #Name, #SE, #Mode); | |
| #define EXPERIMENTAL_FEATURE(Name, Prod, ...) \\ | |
| printf("experimental\\t%s\\t%s\\t\\n", #Name, #Prod); | |
| #define OPTIONAL_LANGUAGE_FEATURE(Name, SE, Desc, ...) \\ | |
| printf("optional\\t%s\\t%s\\t\\n", #Name, #SE); | |
| int main(void) { | |
| #include "Features.def" | |
| return 0; | |
| } | |
| """ | |
| do { | |
| try featuresDef.write(to: defPath, atomically: true, encoding: .utf8) | |
| try driverC.write(to: cPath, atomically: true, encoding: .utf8) | |
| } catch { | |
| die("could not write temp files: \(error)") | |
| } | |
| // SDK include path so <stdio.h> resolves. | |
| let sdk = run("/usr/bin/xcrun", ["--show-sdk-path", "--sdk", "macosx"]) | |
| .out.trimmingCharacters(in: .whitespacesAndNewlines) | |
| guard let cc = xcrunFind("cc") else { die("could not locate cc via xcrun") } | |
| let ccArgs = (sdk.isEmpty ? [] : ["-isysroot", sdk]) + ["-o", binPath.path, cPath.path] | |
| let compile = run(cc, ccArgs) | |
| guard compile.status == 0 else { die("driver failed to compile:\n\(compile.err)") } | |
| let result = run(binPath.path, []) | |
| guard result.status == 0 else { die("driver failed to run:\n\(result.err)") } | |
| return result.out.split(separator: "\n").compactMap { line in | |
| let c = line.split(separator: "\t", omittingEmptySubsequences: false).map(String.init) | |
| guard c.count >= 2 else { return nil } | |
| return Feature(group: c[0], name: c[1], | |
| extra: c.count > 2 ? c[2] : "", | |
| mode: c.count > 3 ? c[3] : "") | |
| } | |
| } | |
| // MARK: - Effective language mode | |
| /// Integer language-mode major (4, 5, 6, …) the package compiles in. SwiftPM | |
| /// derives the default mode from the *major* of the manifest's | |
| /// `swift-tools-version` when `swiftLanguageModes` is unset. `--mode` overrides; | |
| /// nil → unknown (no manifest in cwd). | |
| func effectiveLanguageMode(override: Int?) -> Int? { | |
| if let m = override { return m } | |
| let manifest = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) | |
| .appendingPathComponent("Package.swift") | |
| guard let text = try? String(contentsOf: manifest, encoding: .utf8) else { return nil } | |
| return firstMatch(#"swift-tools-version:?\s*(\d+)"#, in: text).flatMap(Int.init) | |
| } | |
| // MARK: - Rendering (pure: features → text) | |
| let groupTitles = [ | |
| "upcoming": "enableUpcomingFeature", | |
| "experimental": "enableExperimentalFeature", | |
| "optional": "optional language features", | |
| ] | |
| /// Render one group as nested sections: | |
| /// title | |
| /// bucket sub-header (e.g. "redundant — …") | |
| /// new / existing sub-section (only when --new surfaced any new entry) | |
| /// items | |
| /// When nothing is new, the new/existing tier is omitted and items sit directly | |
| /// under the bucket. Pure — returns lines, prints nothing. | |
| func renderGroup(_ group: String, features: [Feature], effectiveMode: Int?) -> [String] { | |
| let title = groupTitles[group] ?? group | |
| let rows = features.filter { $0.group == group } | |
| .map { (feature: $0, bucket: $0.bucket(effectiveMode: effectiveMode)) } | |
| .sorted { | |
| $0.bucket.rank != $1.bucket.rank | |
| ? $0.bucket.rank < $1.bucket.rank | |
| : $0.feature.name.lowercased() < $1.feature.name.lowercased() | |
| } | |
| // The new/existing tier only appears if this group actually has a new entry. | |
| let splitByNewness = rows.contains { $0.feature.isNew } | |
| var lines = ["", "\(title) (\(rows.count))", String(repeating: "─", count: max(title.count, 8))] | |
| /// Emit a sub-header. A blank line precedes it only when it follows item | |
| /// lines or a sibling section — i.e. NOT when it sits directly under its | |
| /// parent header (bucket → first sub-section) or under the group underline. | |
| var lastWasHeader = false | |
| func header(_ indent: String, _ text: String) { | |
| lines.append("\(lastWasHeader ? "" : "\n")\(indent)\(text)") | |
| lastWasHeader = true | |
| } | |
| func items(_ newLines: [String]) { | |
| lines += newLines | |
| if !newLines.isEmpty { lastWasHeader = false } | |
| } | |
| // The very first header in the group should also be flush (nothing above it | |
| // but the underline), so prime the flag as if a header was just emitted. | |
| lastWasHeader = true | |
| /// Emit one item line; suppress the per-row "; new" note when the | |
| /// new/existing sub-section already conveys it. | |
| func itemLine(_ feature: Feature, indent: String, hideNew: Bool) -> String { | |
| let annotation = hideNew ? feature.annotation(includeNew: false) : feature.annotation() | |
| return "\(indent)\(feature.name)\(annotation)" | |
| } | |
| // Walk buckets in sorted order; emit a sub-header per non-empty label. | |
| var lastLabel: String? = nil | |
| for (label, bucketRows) in chunked(rows, by: { $0.bucket.label }) { | |
| let hasBucketHeader = !label.isEmpty | |
| if hasBucketHeader && label != lastLabel { | |
| header(" ", "\(label) (\(bucketRows.count))") | |
| } | |
| lastLabel = label | |
| let itemIndent = hasBucketHeader ? " " : " " | |
| if splitByNewness { | |
| // Within the bucket, split into new then existing, each a sub-section. | |
| let leafIndent = itemIndent + " " | |
| for (sectionLabel, isNew) in [("new", true), ("existing", false)] { | |
| let section = bucketRows.filter { $0.feature.isNew == isNew } | |
| guard !section.isEmpty else { continue } | |
| header(itemIndent, "\(sectionLabel) (\(section.count))") | |
| items(section.map { itemLine($0.feature, indent: leafIndent, hideNew: true) }) | |
| } | |
| } else { | |
| items(bucketRows.map { itemLine($0.feature, indent: itemIndent, hideNew: false) }) | |
| } | |
| } | |
| return lines | |
| } | |
| /// Group adjacent elements by a key, preserving order. Returns (key, items) | |
| /// chunks in first-seen order (input is already sorted, so each key is contiguous). | |
| func chunked<T, K: Equatable>(_ items: [T], by key: (T) -> K) -> [(K, [T])] { | |
| var result: [(K, [T])] = [] | |
| for item in items { | |
| let k = key(item) | |
| if let last = result.indices.last, result[last].0 == k { | |
| result[last].1.append(item) | |
| } else { | |
| result.append((k, [item])) | |
| } | |
| } | |
| return result | |
| } | |
| // MARK: - Main | |
| let opts = parseOptions(Array(CommandLine.arguments.dropFirst())) | |
| let branch = opts.branch ?? branchForActiveToolchain() ?? { | |
| warn("could not parse Swift version; using 'main'"); return "main" | |
| }() | |
| let effMode = effectiveLanguageMode(override: opts.mode) | |
| // Launch both Features.def downloads concurrently — the current branch and | |
| // (for --new/--since) the baseline branch are independent network round-trips. | |
| // `async let` starts each immediately; we await them only where the results | |
| // are needed. fetchBaselineDef returns nil instantly when no diff is requested, | |
| // so the task does no I/O in that case. | |
| async let fetchedTask = fetchFeaturesDef(branch: branch) | |
| async let baselineTask = opts.markNew ? fetchBaselineDef(before: branch, since: opts.since) : nil | |
| let fetched = await fetchedTask | |
| var features = extractFeatures(featuresDef: fetched.text) | |
| // --new / --since: tag features absent from the baseline branch's Features.def. | |
| var diffedAgainst: String? = nil | |
| if opts.markNew { | |
| if let baseline = await baselineTask { | |
| let baselineKeys = Set(extractFeatures(featuresDef: baseline.text).map(\.key)) | |
| features = features.map { | |
| var f = $0; f.isNew = !baselineKeys.contains($0.key); return f | |
| } | |
| diffedAgainst = baseline.branch | |
| } else if let since = opts.since, | |
| releaseVersion(of: branch).map({ compareVersions(since, $0) == .orderedSame }) == true { | |
| // --since equals the current branch: intentional no-op, plain listing. | |
| } else { | |
| let what = opts.since.map { "release/\($0)" } ?? "previous release branch" | |
| warn("no baseline (\(what)) found for \(branch); nothing marked") | |
| } | |
| } | |
| var output = ["Swift feature flags [source: swiftlang/swift @ \(fetched.branch)]"] | |
| if let baseline = diffedAgainst { | |
| let flag = opts.since == nil ? "--new" : "--since" | |
| output.append("Marking entries new since \(baseline) (\(flag))") | |
| } | |
| output.append(effMode.map { mode in | |
| let source = opts.mode == nil ? " (from ./Package.swift)" : " (--mode)" | |
| return "Judging upcoming flags against language mode \(mode)\(source)" | |
| } ?? "No ./Package.swift found — pass --mode N to mark upcoming flags redundant/opt-in") | |
| for group in ["upcoming", "experimental", "optional"] where opts.groups.contains(group) { | |
| output += renderGroup(group, features: features, effectiveMode: effMode) | |
| } | |
| print(output.joined(separator: "\n")) | |
| print("") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Swift feature flags [source: swiftlang/swift @ release/6.3] | |
| Marking entries new since release/6.2 (--new) | |
| Judging upcoming flags against language mode 6 (from ./Package.swift) | |
| enableUpcomingFeature (21) | |
| ───────────────────── | |
| redundant — already default in mode 6 (flag is a no-op) (15) | |
| BareSlashRegexLiterals (SE-354) | |
| ConciseMagicFile (SE-274) | |
| DeprecateApplicationMain (SE-383) | |
| DisableOutwardActorInference (SE-401) | |
| DynamicActorIsolation (SE-423) | |
| ForwardTrailingClosures (SE-286) | |
| GlobalActorIsolatedTypesUsability (SE-0434) | |
| GlobalConcurrency (SE-412) | |
| ImplicitOpenExistentials (SE-352) | |
| ImportObjcForwardDeclarations (SE-384) | |
| InferSendableFromCaptures (SE-418) | |
| IsolatedDefaultValues (SE-411) | |
| NonfrozenEnumExhaustivity (SE-192) | |
| RegionBasedIsolation (SE-414) | |
| StrictConcurrency (SE-0337) | |
| opt-in — default in mode 7, required until then (6) | |
| ExistentialAny (SE-335) | |
| ImmutableWeakCaptures (SE-481) | |
| InferIsolatedConformances (SE-470) | |
| InternalImportsByDefault (SE-409) | |
| MemberImportVisibility (SE-444) | |
| NonisolatedNonsendingByDefault (SE-461) | |
| enableExperimentalFeature (86) | |
| ───────────────────────── | |
| available on release toolchains (48) | |
| new (6) | |
| AnyAppleOSAvailability | |
| CheckImplementationOnly | |
| DeferredCodeGen | |
| ImportMacroAliases | |
| StandaloneSwiftAvailability | |
| SwiftRuntimeAvailability | |
| existing (42) | |
| AccessLevelOnImport | |
| AddressableParameters | |
| AddressableTypes | |
| AllowRuntimeSymbolDeclarations | |
| AssumeResilientCxxTypes | |
| BuiltinModule | |
| ClosureBodyMacro | |
| ClosureIsolation | |
| CompileTimeValues | |
| ConcurrencySyntaxSugar | |
| ConsumeSelfInDeinit | |
| CoroutineAccessors | |
| CustomAvailability | |
| DebugDescriptionMacro | |
| Embedded | |
| Extern | |
| GroupActorErrors | |
| ImportNonPublicCxxMembers | |
| LayoutStringValueWitnesses | |
| LayoutStringValueWitnessesInstantiation | |
| LifetimeDependence | |
| Lifetimes | |
| MacrosOnImports | |
| MoveOnlyClasses | |
| MoveOnlyEnumDeinits | |
| MoveOnlyPartialReinitialization | |
| MoveOnlyTuples | |
| NoExplicitNonIsolated | |
| NoImplicitCopy | |
| ObjCImplementationWithResilientStorage | |
| OldOwnershipOperatorSpellings | |
| OpaqueTypeErasure | |
| PlaygroundExtendedCallbacks | |
| RawLayout | |
| ReferenceBindings | |
| SafeInteropWrappers | |
| SE427NoInferenceOnExtension | |
| Sensitive | |
| StaticExclusiveOnly | |
| StructLetDestructuring | |
| SuppressedAssociatedTypes | |
| Volatile | |
| asserts/development toolchains only (38) | |
| new (7) | |
| BorrowAndMutateAccessors | |
| CheckImplementationOnlyStrict | |
| CompileTimeValuesPreview | |
| EmbeddedExistentials | |
| ForExpressions | |
| ManualOwnership | |
| TildeSendable | |
| existing (31) | |
| AdditiveArithmeticDerivedConformances | |
| AllowNonResilientAccessInPackage | |
| BuiltinMacros | |
| ClientBypassResilientAccessInPackage | |
| CodeItemMacros | |
| CoroutineAccessorsUnwindOnCallerError | |
| DefaultIsolationPerFile | |
| DifferentiableProgramming | |
| DoExpressions | |
| ExtractConstantsFromMembers | |
| FlowSensitiveConcurrencyCaptures | |
| ForwardModeDifferentiation | |
| FullTypedThrows | |
| GenerateBindingsForThrowingFunctionsInCXX | |
| ImplicitLastExprResults | |
| ImplicitSome | |
| KeyPathWithMethodMembers | |
| LazyImmediate | |
| NamedOpaqueTypes | |
| PackageCMO | |
| ParserASTGen | |
| ParserRoundTrip | |
| ParserValidation | |
| PreambleMacros | |
| ReinitializeConsumeInMultiBlockDefer | |
| SameElementRequirements | |
| StaticAssert | |
| ThenStatements | |
| TrailingComma | |
| TupleConformances | |
| UnqualifiedLookupValidation | |
| optional language features (2) | |
| ────────────────────────── | |
| new (1) | |
| LibraryEvolution | |
| existing (1) | |
| StrictMemorySafety (SE-458) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment