Skip to content

Commit 1a86cd9

Browse files
committed
AST: Introduce a Swift runtime availability domain.
Add support for the `Swift` availability domain, which represents availability with respect to the Swift runtime. Use of this domain is restricted by the experimental feature `SwiftRuntimeAvailability`.
1 parent 2d8465b commit 1a86cd9

19 files changed

+213
-48
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class AvailabilityDomain final {
5151
/// Represents availability with respect to Swift language mode.
5252
SwiftLanguageMode,
5353

54+
/// Represents availability with respect to the Swift runtime.
55+
SwiftRuntime,
56+
5457
/// Represents PackageDescription availability.
5558
PackageDescription,
5659

@@ -144,6 +147,10 @@ class AvailabilityDomain final {
144147
return AvailabilityDomain(Kind::SwiftLanguageMode);
145148
}
146149

150+
static AvailabilityDomain forSwiftRuntime() {
151+
return AvailabilityDomain(Kind::SwiftRuntime);
152+
}
153+
147154
static AvailabilityDomain forPackageDescription() {
148155
return AvailabilityDomain(Kind::PackageDescription);
149156
}
@@ -185,6 +192,8 @@ class AvailabilityDomain final {
185192
return getKind() == Kind::SwiftLanguageMode;
186193
}
187194

195+
bool isSwiftRuntime() const { return getKind() == Kind::SwiftRuntime; }
196+
188197
bool isPackageDescription() const {
189198
return getKind() == Kind::PackageDescription;
190199
}
@@ -236,6 +245,10 @@ class AvailabilityDomain final {
236245
bool isActivePlatform(const ASTContext &ctx,
237246
bool forTargetVariant = false) const;
238247

248+
/// Returns true if availability in this domain must be specified alone in
249+
/// `@available` attributes and `if #available` queries.
250+
bool mustBeSpecifiedAlone() const;
251+
239252
/// Returns the domain's minimum available range for type checking. For
240253
/// example, for the domain of the platform that compilation is targeting,
241254
/// this version is specified with the `-target` option. For the Swift

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ namespace swift {
188188
/// Swift runtime version to compile for.
189189
version::Version RuntimeVersion = version::Version::getCurrentLanguageVersion();
190190

191+
/// The minimum Swift runtime version that the progam can be deployed to.
192+
version::Version MinSwiftRuntimeVersion;
193+
191194
/// PackageDescription version to compile for.
192195
version::Version PackageDescriptionVersion;
193196

include/swift/Option/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ def language_mode : Separate<["-"], "language-mode">,
298298
MetaVarName<"<mode>">,
299299
Alias<swift_version>;
300300

301+
def min_swift_runtime_version
302+
: Separate<["-"], "min-swift-runtime-version">,
303+
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
304+
HelpText<"The minimum Swift runtime version "
305+
"that will be available at runtime">,
306+
MetaVarName<"<vers>">;
307+
301308
def package_description_version: Separate<["-"], "package-description-version">,
302309
Flags<[FrontendOption, HelpHidden, ModuleInterfaceOption]>,
303310
HelpText<"The version number to be applied on the input for the PackageDescription availability kind">,

lib/AST/Availability.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ computeDeclRuntimeAvailability(const Decl *decl) {
640640
if (!domain.isActive(ctx) && !isTargetDomain)
641641
continue;
642642

643+
// FIXME: [runtime availability] Update this?
643644
if (!domain.isRoot())
644645
continue;
645646

@@ -855,7 +856,8 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
855856
return std::nullopt;
856857
}
857858

858-
if (domain->isSwiftLanguageMode() || domain->isPackageDescription()) {
859+
if (domain->isSwiftLanguageMode() || domain->isPackageDescription() ||
860+
domain->isSwiftRuntime()) {
859861
switch (attr->getKind()) {
860862
case AvailableAttr::Kind::Deprecated:
861863
diags.diagnose(attrLoc,

lib/AST/AvailabilityConstraint.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ domainCanBeUnconditionallyUnavailableAtRuntime(AvailabilityDomain domain,
343343
return domain.isActive(ctx);
344344

345345
case AvailabilityDomain::Kind::SwiftLanguageMode:
346+
case AvailabilityDomain::Kind::SwiftRuntime:
346347
case AvailabilityDomain::Kind::PackageDescription:
347348
return false;
348349

@@ -370,6 +371,7 @@ domainIsUnavailableAtRuntimeIfUnintroduced(AvailabilityDomain domain,
370371
case AvailabilityDomain::Kind::Universal:
371372
case AvailabilityDomain::Kind::Platform:
372373
case AvailabilityDomain::Kind::SwiftLanguageMode:
374+
case AvailabilityDomain::Kind::SwiftRuntime:
373375
case AvailabilityDomain::Kind::PackageDescription:
374376
return false;
375377

lib/AST/AvailabilityDomain.cpp

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ AvailabilityDomain::builtinDomainForString(StringRef string,
112112
.Case("*", AvailabilityDomain::forUniversal())
113113
.Case("swift", AvailabilityDomain::forSwiftLanguageMode())
114114
.Case("SwiftLanguageMode", AvailabilityDomain::forSwiftLanguageMode())
115+
.Case("Swift", AvailabilityDomain::forSwiftRuntime())
115116
.Case("_PackageDescription",
116117
AvailabilityDomain::forPackageDescription())
117118
.Default(std::nullopt);
@@ -131,6 +132,7 @@ bool AvailabilityDomain::isVersioned() const {
131132
case Kind::Embedded:
132133
return false;
133134
case Kind::SwiftLanguageMode:
135+
case Kind::SwiftRuntime:
134136
case Kind::PackageDescription:
135137
case Kind::Platform:
136138
return true;
@@ -151,6 +153,12 @@ bool AvailabilityDomain::isVersionValid(
151153
case Kind::SwiftLanguageMode:
152154
case Kind::PackageDescription:
153155
return true;
156+
case Kind::SwiftRuntime:
157+
// Swift 5.0 is the first ABI stable Swift runtime version.
158+
if (version.getMajor() < 5)
159+
return false;
160+
return true;
161+
154162
case Kind::Platform:
155163
if (auto osType = tripleOSTypeForPlatform(getPlatformKind()))
156164
return llvm::Triple::isValidVersionForOS(*osType, version);
@@ -167,6 +175,7 @@ bool AvailabilityDomain::supportsContextRefinement() const {
167175
case Kind::SwiftLanguageMode:
168176
case Kind::PackageDescription:
169177
return false;
178+
case Kind::SwiftRuntime:
170179
case Kind::Platform:
171180
case Kind::Custom:
172181
return true;
@@ -180,6 +189,7 @@ bool AvailabilityDomain::supportsQueries() const {
180189
case Kind::SwiftLanguageMode:
181190
case Kind::PackageDescription:
182191
return false;
192+
case Kind::SwiftRuntime:
183193
case Kind::Platform:
184194
case Kind::Custom:
185195
return true;
@@ -194,6 +204,8 @@ bool AvailabilityDomain::isActive(const ASTContext &ctx,
194204
case Kind::PackageDescription:
195205
case Kind::Embedded:
196206
return true;
207+
case Kind::SwiftRuntime:
208+
return ctx.LangOpts.hasFeature(Feature::SwiftRuntimeAvailability);
197209
case Kind::Platform:
198210
return isPlatformActive(getPlatformKind(), ctx.LangOpts, forTargetVariant);
199211
case Kind::Custom:
@@ -211,6 +223,24 @@ bool AvailabilityDomain::isActivePlatform(const ASTContext &ctx,
211223
return isActive(ctx, forTargetVariant);
212224
}
213225

226+
bool AvailabilityDomain::mustBeSpecifiedAlone() const {
227+
switch (getKind()) {
228+
case Kind::Universal:
229+
case Kind::SwiftLanguageMode:
230+
case Kind::PackageDescription:
231+
case Kind::Embedded:
232+
case Kind::Custom:
233+
return true;
234+
case Kind::SwiftRuntime:
235+
case Kind::Platform:
236+
// Platform and Swift runtime availability specifications can appear
237+
// together, e.g. `@available(Swift 6, macOS 15, iOS 18, *)`.
238+
// If there are ever multiple disjoint groups of domains that may be
239+
// specified together in the future, this will need to be re-designed.
240+
return false;
241+
}
242+
}
243+
214244
static std::optional<llvm::VersionTuple>
215245
getDeploymentVersion(const AvailabilityDomain &domain, const ASTContext &ctx) {
216246
switch (domain.getKind()) {
@@ -222,6 +252,10 @@ getDeploymentVersion(const AvailabilityDomain &domain, const ASTContext &ctx) {
222252
return ctx.LangOpts.EffectiveLanguageVersion;
223253
case AvailabilityDomain::Kind::PackageDescription:
224254
return ctx.LangOpts.PackageDescriptionVersion;
255+
case AvailabilityDomain::Kind::SwiftRuntime:
256+
if (!ctx.LangOpts.hasFeature(Feature::SwiftRuntimeAvailability))
257+
return std::nullopt;
258+
return ctx.LangOpts.MinSwiftRuntimeVersion;
225259
case AvailabilityDomain::Kind::Platform:
226260
if (domain.isActive(ctx))
227261
return ctx.LangOpts.getMinPlatformVersion();
@@ -256,6 +290,9 @@ llvm::StringRef AvailabilityDomain::getNameForDiagnostics() const {
256290
case Kind::Universal:
257291
return "*";
258292
case Kind::SwiftLanguageMode:
293+
// FIXME: [runtime availability] Render language mode diags differently.
294+
return "Swift";
295+
case Kind::SwiftRuntime:
259296
return "Swift";
260297
case Kind::PackageDescription:
261298
return "PackageDescription";
@@ -274,6 +311,8 @@ llvm::StringRef AvailabilityDomain::getNameForAttributePrinting() const {
274311
return "*";
275312
case Kind::SwiftLanguageMode:
276313
return "swift";
314+
case Kind::SwiftRuntime:
315+
return "Swift";
277316
case Kind::PackageDescription:
278317
return "_PackageDescription";
279318
case Kind::Embedded:
@@ -308,6 +347,8 @@ bool AvailabilityDomain::contains(const AvailabilityDomain &other) const {
308347
case Kind::Embedded:
309348
case Kind::Custom:
310349
return other == *this;
350+
case Kind::SwiftRuntime:
351+
return other.isPlatform() || other == *this;
311352
case Kind::Platform:
312353
if (getPlatformKind() == other.getPlatformKind())
313354
return true;
@@ -321,6 +362,7 @@ bool AvailabilityDomain::isRoot() const {
321362
case AvailabilityDomain::Kind::Universal:
322363
case AvailabilityDomain::Kind::Embedded:
323364
case AvailabilityDomain::Kind::SwiftLanguageMode:
365+
case AvailabilityDomain::Kind::SwiftRuntime:
324366
case AvailabilityDomain::Kind::PackageDescription:
325367
return true;
326368
case AvailabilityDomain::Kind::Platform:
@@ -406,6 +448,7 @@ AvailabilityDomain AvailabilityDomain::copy(ASTContext &ctx) const {
406448
switch (getKind()) {
407449
case Kind::Universal:
408450
case Kind::SwiftLanguageMode:
451+
case Kind::SwiftRuntime:
409452
case Kind::PackageDescription:
410453
case Kind::Embedded:
411454
case Kind::Platform:
@@ -428,6 +471,7 @@ bool StableAvailabilityDomainComparator::operator()(
428471
switch (lhsKind) {
429472
case AvailabilityDomain::Kind::Universal:
430473
case AvailabilityDomain::Kind::SwiftLanguageMode:
474+
case AvailabilityDomain::Kind::SwiftRuntime:
431475
case AvailabilityDomain::Kind::PackageDescription:
432476
case AvailabilityDomain::Kind::Embedded:
433477
return false;
@@ -504,31 +548,41 @@ AvailabilityDomainOrIdentifier::lookUpInDeclContext(
504548
return std::nullopt;
505549
}
506550

507-
if (!declContext->isInSwiftinterface()) {
508-
if (domain->isCustom() && !hasCustomAvailability) {
509-
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
510-
"CustomAvailability");
511-
return std::nullopt;
551+
// The remaining diagnostics are suppressed in .swiftinterfaces.
552+
if (declContext->isInSwiftinterface())
553+
return domain;
554+
555+
// Use of custom domains requires the 'CustomAvailability' feature.
556+
if (domain->isCustom() && !hasCustomAvailability) {
557+
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
558+
"CustomAvailability");
559+
return std::nullopt;
560+
}
561+
562+
// Use of the 'Swift' domain requires the 'SwiftRuntimeAvailability' feature.
563+
if (!hasSwiftRuntimeAvailability && domain->isSwiftRuntime()) {
564+
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
565+
"SwiftRuntimeAvailability");
566+
return std::nullopt;
567+
}
568+
569+
if (domain->isSwiftLanguageMode()) {
570+
// When the 'SwiftRuntimeAvailability' feature is enabled, the 'swift'
571+
// domain spelling is deprecated in favor of 'SwiftLanguageMode'.
572+
if (hasSwiftRuntimeAvailability && identifier.str() == "swift") {
573+
diags
574+
.diagnose(loc, diag::availability_domain_renamed, identifier,
575+
"SwiftLanguageMode")
576+
.fixItReplace(SourceRange(loc), "SwiftLanguageMode");
512577
}
513578

514-
if (domain->isSwiftLanguageMode()) {
515-
// 'swift' -> 'SwiftLanguageMode'
516-
if (hasSwiftRuntimeAvailability && identifier.str() == "swift") {
517-
diags
518-
.diagnose(loc, diag::availability_domain_renamed, identifier,
519-
"SwiftLanguageMode")
520-
.fixItReplace(SourceRange(loc), "SwiftLanguageMode");
521-
}
522-
523-
if (!hasSwiftRuntimeAvailability &&
524-
identifier.str() == "SwiftLanguageMode") {
525-
// This diagnostic ("Swift requires '-enable-experimental-feature
526-
// SwiftRuntimeAvailability'") is confusing but it's also temporary,
527-
// assuming the experimental feature becomes official.
528-
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
529-
"SwiftRuntimeAvailability");
530-
return std::nullopt;
531-
}
579+
// Use of the 'SwiftLanguageMode' domain spelling requires the
580+
// 'SwiftRuntimeAvailability' feature.
581+
if (!hasSwiftRuntimeAvailability &&
582+
identifier.str() == "SwiftLanguageMode") {
583+
diags.diagnose(loc, diag::availability_domain_requires_feature, *domain,
584+
"SwiftRuntimeAvailability");
585+
return std::nullopt;
532586
}
533587
}
534588

lib/AST/AvailabilityQuery.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ FuncDecl *AvailabilityQuery::getDynamicQueryDeclAndArguments(
133133
switch (domain.getKind()) {
134134
case AvailabilityDomain::Kind::Universal:
135135
case AvailabilityDomain::Kind::SwiftLanguageMode:
136+
case AvailabilityDomain::Kind::SwiftRuntime:
136137
case AvailabilityDomain::Kind::PackageDescription:
137138
case AvailabilityDomain::Kind::Embedded:
138139
return nullptr;

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -869,11 +869,12 @@ class AvailabilityScopeBuilder : private ASTWalker {
869869
return AvailabilityQuery::dynamic(variantSpec->getDomain(), primaryRange,
870870
variantRange);
871871

872+
case AvailabilityDomain::Kind::SwiftRuntime:
872873
case AvailabilityDomain::Kind::Platform:
873-
// Platform checks are always dynamic. The SIL optimizer is responsible
874-
// eliminating these checks when it can prove that they can never fail
875-
// (due to the deployment target). We can't perform that analysis here
876-
// because it may depend on inlining.
874+
// Platform and Swift runtime checks are always dynamic. The SIL optimizer
875+
// is responsible eliminating these checks when it can prove that they can
876+
// never fail (due to the deployment target). We can't perform that
877+
// analysis here because it may depend on inlining.
877878
return AvailabilityQuery::dynamic(domain, primaryRange, variantRange);
878879
case AvailabilityDomain::Kind::Custom:
879880
auto customDomain = domain.getCustomDomain();

lib/Frontend/CompilerInvocation.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,15 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
10461046
// HadError = true;
10471047
}
10481048

1049+
if (auto A = Args.getLastArg(OPT_min_swift_runtime_version)) {
1050+
if (auto vers = VersionParser::parseVersionString(A->getValue(),
1051+
SourceLoc(), &Diags)) {
1052+
Opts.MinSwiftRuntimeVersion = *vers;
1053+
} else {
1054+
return true;
1055+
}
1056+
}
1057+
10491058
if (auto A = Args.getLastArg(OPT_package_description_version)) {
10501059
auto vers =
10511060
VersionParser::parseVersionString(A->getValue(), SourceLoc(), &Diags);

lib/Sema/MiscDiagnostics.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5131,7 +5131,7 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
51315131
info->isUnavailability() ? "#unavailable" : "#available";
51325132

51335133
bool hasValidSpecs = false;
5134-
bool allValidSpecsArePlatform = true;
5134+
bool wildcardRequiredInList = false;
51355135
std::optional<SourceLoc> wildcardLoc;
51365136
llvm::SmallSet<AvailabilityDomain, 8> seenDomains;
51375137
for (auto spec : info->getSemanticAvailabilitySpecs(DC)) {
@@ -5144,14 +5144,15 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
51445144
auto domain = spec.getDomain();
51455145
auto loc = parsedSpec->getStartLoc();
51465146
bool hasVersion = !spec.getVersion().empty();
5147+
bool mustBeSpecifiedAlone = domain.mustBeSpecifiedAlone();
51475148

51485149
if (!domain.supportsQueries()) {
51495150
diags.diagnose(loc, diag::availability_query_not_allowed, domain,
51505151
hasVersion, queryName);
51515152
return true;
51525153
}
51535154

5154-
if (!domain.isPlatform() && info->getQueries().size() > 1) {
5155+
if (mustBeSpecifiedAlone && info->getQueries().size() > 1) {
51555156
diags.diagnose(loc, diag::availability_must_occur_alone, domain,
51565157
hasVersion);
51575158
return true;
@@ -5199,8 +5200,8 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
51995200
}
52005201

52015202
hasValidSpecs = true;
5202-
if (!domain.isPlatform())
5203-
allValidSpecsArePlatform = false;
5203+
if (!mustBeSpecifiedAlone)
5204+
wildcardRequiredInList = true;
52045205
}
52055206

52065207
if (info->isUnavailability()) {
@@ -5210,7 +5211,7 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
52105211
diag::unavailability_query_wildcard_not_required)
52115212
.fixItRemove(*wildcardLoc);
52125213
}
5213-
} else if (!wildcardLoc && hasValidSpecs && allValidSpecsArePlatform) {
5214+
} else if (!wildcardLoc && hasValidSpecs && wildcardRequiredInList) {
52145215
if (info->getQueries().size() > 0) {
52155216
auto insertLoc = info->getQueries().back()->getSourceRange().End;
52165217
diags.diagnose(insertLoc, diag::availability_query_wildcard_required)

0 commit comments

Comments
 (0)