Skip to content

Commit 0ba825e

Browse files
authored
Merge pull request #84240 from tshortli/always-enabled-availability-domains
AST: Introduce an "always enabled" custom availability domain kind
2 parents d2a03b4 + d2682f7 commit 0ba825e

15 files changed

+272
-22
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,12 @@ class AvailabilityDomain final {
226226

227227
/// Returns true if this domain is considered active in the current
228228
/// compilation context.
229-
bool isActive(const ASTContext &ctx) const;
229+
bool isActive(const ASTContext &ctx, bool forTargetVariant = false) const;
230230

231231
/// Returns true if this domain is a platform domain and is considered active
232232
/// in the current compilation context.
233-
bool isActivePlatform(const ASTContext &ctx) const;
233+
bool isActivePlatform(const ASTContext &ctx,
234+
bool forTargetVariant = false) const;
234235

235236
/// Returns the domain's minimum available range for type checking. For
236237
/// example, for the domain of the platform that compilation is targeting,
@@ -332,6 +333,9 @@ class CustomAvailabilityDomain : public llvm::FoldingSetNode {
332333
enum class Kind {
333334
/// A domain that is known to be enabled at compile time.
334335
Enabled,
336+
/// A domain that is known to be enabled at compile time and is also assumed
337+
/// to be enabled for all deployments.
338+
AlwaysEnabled,
335339
/// A domain that is known to be disabled at compile time.
336340
Disabled,
337341
/// A domain with an enablement state that must be queried at runtime.

include/swift/Frontend/FrontendOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,8 @@ class FrontendOptions {
615615
struct CustomAvailabilityDomains {
616616
/// Domains defined with `-define-enabled-availability-domain=`.
617617
llvm::SmallVector<std::string> EnabledDomains;
618+
/// Domains defined with `-define-always-enabled-availability-domain=`.
619+
llvm::SmallVector<std::string> AlwaysEnabledDomains;
618620
/// Domains defined with `-define-disabled-availability-domain=`.
619621
llvm::SmallVector<std::string> DisabledDomains;
620622
/// Domains defined with `-define-dynamic-availability-domain=`.

include/swift/Option/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,14 @@ def define_enabled_availability_domain : Separate<["-"], "define-enabled-availab
576576
HelpText<"Defines a custom availability domain that is available at compile time">,
577577
MetaVarName<"<domain>">;
578578

579+
def define_always_enabled_availability_domain
580+
: Separate<["-"], "define-always-enabled-availability-domain">,
581+
Flags<[HelpHidden, FrontendOption, NoInteractiveOption,
582+
ModuleInterfaceOptionIgnorable]>,
583+
HelpText<"Defines a custom availability domain that is available for all "
584+
"deployments">,
585+
MetaVarName<"<domain>">;
586+
579587
def define_disabled_availability_domain : Separate<["-"], "define-disabled-availability-domain">,
580588
Flags<[HelpHidden, FrontendOption, NoInteractiveOption, ModuleInterfaceOptionIgnorable]>,
581589
HelpText<"Defines a custom availability domain that is unavailable at compile time">,

lib/AST/AvailabilityConstraint.cpp

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,15 @@ getAvailabilityConstraintForAttr(const Decl *decl,
204204

205205
auto &ctx = decl->getASTContext();
206206
auto domain = attr.getDomain();
207-
auto deploymentRange = domain.getDeploymentRange(ctx);
208207
bool domainSupportsRefinement = domain.supportsContextRefinement();
209-
std::optional<AvailabilityRange> availableRange =
210-
domainSupportsRefinement ? context.getAvailabilityRange(domain, ctx)
211-
: deploymentRange;
208+
209+
// Compute the available range in the given context. If there is no explicit
210+
// range defined by the context, use the deployment range as fallback.
211+
std::optional<AvailabilityRange> availableRange;
212+
if (domainSupportsRefinement)
213+
availableRange = context.getAvailabilityRange(domain, ctx);
214+
if (!availableRange)
215+
availableRange = domain.getDeploymentRange(ctx);
212216

213217
// Is the decl obsoleted in this context?
214218
if (auto obsoletedRange = attr.getObsoletedRange(ctx)) {
@@ -323,24 +327,78 @@ swift::getAvailabilityConstraintForDeclInDomain(
323327
return std::nullopt;
324328
}
325329

330+
/// Returns true if unsatisfied `@available(..., unavailable)` constraints for
331+
/// \p domain make code unreachable at runtime
332+
static bool
333+
domainCanBeUnconditionallyUnavailableAtRuntime(AvailabilityDomain domain,
334+
const ASTContext &ctx) {
335+
switch (domain.getKind()) {
336+
case AvailabilityDomain::Kind::Universal:
337+
return true;
338+
339+
case AvailabilityDomain::Kind::Platform:
340+
if (ctx.LangOpts.TargetVariant &&
341+
domain.isActive(ctx, /*forTargetVariant=*/true))
342+
return true;
343+
return domain.isActive(ctx);
344+
345+
case AvailabilityDomain::Kind::SwiftLanguage:
346+
case AvailabilityDomain::Kind::PackageDescription:
347+
return false;
348+
349+
case AvailabilityDomain::Kind::Embedded:
350+
return ctx.LangOpts.hasFeature(Feature::Embedded);
351+
352+
case AvailabilityDomain::Kind::Custom:
353+
switch (domain.getCustomDomain()->getKind()) {
354+
case CustomAvailabilityDomain::Kind::Enabled:
355+
case CustomAvailabilityDomain::Kind::AlwaysEnabled:
356+
return true;
357+
case CustomAvailabilityDomain::Kind::Disabled:
358+
case CustomAvailabilityDomain::Kind::Dynamic:
359+
return false;
360+
}
361+
}
362+
}
363+
364+
/// Returns true if unsatisfied introduction constraints for \p domain make
365+
/// code unreachable at runtime.
366+
static bool
367+
domainIsUnavailableAtRuntimeIfUnintroduced(AvailabilityDomain domain,
368+
const ASTContext &ctx) {
369+
switch (domain.getKind()) {
370+
case AvailabilityDomain::Kind::Universal:
371+
case AvailabilityDomain::Kind::Platform:
372+
case AvailabilityDomain::Kind::SwiftLanguage:
373+
case AvailabilityDomain::Kind::PackageDescription:
374+
return false;
375+
376+
case AvailabilityDomain::Kind::Embedded:
377+
return !ctx.LangOpts.hasFeature(Feature::Embedded);
378+
379+
case AvailabilityDomain::Kind::Custom:
380+
switch (domain.getCustomDomain()->getKind()) {
381+
case CustomAvailabilityDomain::Kind::Enabled:
382+
case CustomAvailabilityDomain::Kind::AlwaysEnabled:
383+
case CustomAvailabilityDomain::Kind::Dynamic:
384+
return false;
385+
case CustomAvailabilityDomain::Kind::Disabled:
386+
return true;
387+
}
388+
}
389+
}
390+
326391
static bool constraintIndicatesRuntimeUnavailability(
327392
const AvailabilityConstraint &constraint, const ASTContext &ctx) {
328-
std::optional<CustomAvailabilityDomain::Kind> customDomainKind;
329-
if (auto customDomain = constraint.getDomain().getCustomDomain())
330-
customDomainKind = customDomain->getKind();
331-
393+
auto domain = constraint.getDomain();
332394
switch (constraint.getReason()) {
333395
case AvailabilityConstraint::Reason::UnavailableUnconditionally:
334-
if (customDomainKind)
335-
return customDomainKind == CustomAvailabilityDomain::Kind::Enabled;
336-
return true;
396+
return domainCanBeUnconditionallyUnavailableAtRuntime(domain, ctx);
337397
case AvailabilityConstraint::Reason::UnavailableObsolete:
338398
case AvailabilityConstraint::Reason::UnavailableUnintroduced:
339399
return false;
340400
case AvailabilityConstraint::Reason::Unintroduced:
341-
if (customDomainKind)
342-
return customDomainKind == CustomAvailabilityDomain::Kind::Disabled;
343-
return false;
401+
return domainIsUnavailableAtRuntimeIfUnintroduced(domain, ctx);
344402
}
345403
}
346404

lib/AST/AvailabilityDomain.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ getCustomDomainKind(clang::FeatureAvailKind featureAvailKind) {
3333
return CustomAvailabilityDomain::Kind::Disabled;
3434
case clang::FeatureAvailKind::Dynamic:
3535
return CustomAvailabilityDomain::Kind::Dynamic;
36+
case clang::FeatureAvailKind::AlwaysAvailable:
37+
return CustomAvailabilityDomain::Kind::AlwaysEnabled;
3638
default:
3739
llvm::report_fatal_error("unexpected kind");
3840
}
@@ -57,6 +59,7 @@ customDomainForClangDecl(ValueDecl *decl) {
5759
case clang::FeatureAvailKind::Available:
5860
case clang::FeatureAvailKind::Unavailable:
5961
case clang::FeatureAvailKind::Dynamic:
62+
case clang::FeatureAvailKind::AlwaysAvailable:
6063
break;
6164
default:
6265
return nullptr;
@@ -181,27 +184,29 @@ bool AvailabilityDomain::supportsQueries() const {
181184
}
182185
}
183186

184-
bool AvailabilityDomain::isActive(const ASTContext &ctx) const {
187+
bool AvailabilityDomain::isActive(const ASTContext &ctx,
188+
bool forTargetVariant) const {
185189
switch (getKind()) {
186190
case Kind::Universal:
187191
case Kind::SwiftLanguage:
188192
case Kind::PackageDescription:
189193
case Kind::Embedded:
190194
return true;
191195
case Kind::Platform:
192-
return isPlatformActive(getPlatformKind(), ctx.LangOpts);
196+
return isPlatformActive(getPlatformKind(), ctx.LangOpts, forTargetVariant);
193197
case Kind::Custom:
194198
// For now, custom domains are always active but it's conceivable that in
195199
// the future someone might want to define a domain but leave it inactive.
196200
return true;
197201
}
198202
}
199203

200-
bool AvailabilityDomain::isActivePlatform(const ASTContext &ctx) const {
204+
bool AvailabilityDomain::isActivePlatform(const ASTContext &ctx,
205+
bool forTargetVariant) const {
201206
if (!isPlatform())
202207
return false;
203208

204-
return isActive(ctx);
209+
return isActive(ctx, forTargetVariant);
205210
}
206211

207212
static std::optional<llvm::VersionTuple>
@@ -224,8 +229,23 @@ getDeploymentVersion(const AvailabilityDomain &domain, const ASTContext &ctx) {
224229

225230
std::optional<AvailabilityRange>
226231
AvailabilityDomain::getDeploymentRange(const ASTContext &ctx) const {
227-
if (auto version = getDeploymentVersion(*this, ctx))
228-
return AvailabilityRange{*version};
232+
if (isVersioned()) {
233+
if (auto version = getDeploymentVersion(*this, ctx))
234+
return AvailabilityRange{*version};
235+
236+
return std::nullopt;
237+
}
238+
239+
if (auto customDomain = getCustomDomain()) {
240+
switch (customDomain->getKind()) {
241+
case CustomAvailabilityDomain::Kind::AlwaysEnabled:
242+
return AvailabilityRange::alwaysAvailable();
243+
case CustomAvailabilityDomain::Kind::Enabled:
244+
case CustomAvailabilityDomain::Kind::Disabled:
245+
case CustomAvailabilityDomain::Kind::Dynamic:
246+
return std::nullopt;
247+
}
248+
}
229249
return std::nullopt;
230250
}
231251

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
881881

882882
switch (customDomain->getKind()) {
883883
case CustomAvailabilityDomain::Kind::Enabled:
884+
case CustomAvailabilityDomain::Kind::AlwaysEnabled:
884885
return AvailabilityQuery::constant(domain, true);
885886
case CustomAvailabilityDomain::Kind::Disabled:
886887
return AvailabilityQuery::constant(domain, false);

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ bool ArgsToFrontendOptionsConverter::computeAvailabilityDomains() {
586586

587587
for (const Arg *A :
588588
Args.filtered_reverse(OPT_define_enabled_availability_domain,
589+
OPT_define_always_enabled_availability_domain,
589590
OPT_define_disabled_availability_domain,
590591
OPT_define_dynamic_availability_domain)) {
591592
std::string domain = A->getValue();
@@ -602,6 +603,8 @@ bool ArgsToFrontendOptionsConverter::computeAvailabilityDomains() {
602603
auto &option = A->getOption();
603604
if (option.matches(OPT_define_enabled_availability_domain))
604605
Opts.AvailabilityDomains.EnabledDomains.emplace_back(domain);
606+
if (option.matches(OPT_define_always_enabled_availability_domain))
607+
Opts.AvailabilityDomains.AlwaysEnabledDomains.emplace_back(domain);
605608
else if (option.matches(OPT_define_disabled_availability_domain))
606609
Opts.AvailabilityDomains.DisabledDomains.emplace_back(domain);
607610
else if (option.matches(OPT_define_dynamic_availability_domain))

lib/Frontend/Frontend.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,9 @@ static void configureAvailabilityDomains(const ASTContext &ctx,
14551455

14561456
for (auto enabled : opts.AvailabilityDomains.EnabledDomains)
14571457
createAndInsertDomain(enabled, CustomAvailabilityDomain::Kind::Enabled);
1458+
for (auto alwaysEnabled : opts.AvailabilityDomains.AlwaysEnabledDomains)
1459+
createAndInsertDomain(alwaysEnabled,
1460+
CustomAvailabilityDomain::Kind::AlwaysEnabled);
14581461
for (auto disabled : opts.AvailabilityDomains.DisabledDomains)
14591462
createAndInsertDomain(disabled, CustomAvailabilityDomain::Kind::Disabled);
14601463
for (auto dynamic : opts.AvailabilityDomains.DynamicDomains)

test/Availability/availability_custom_domains.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %target-typecheck-verify-swift \
22
// RUN: -enable-experimental-feature CustomAvailability \
33
// RUN: -define-enabled-availability-domain EnabledDomain \
4+
// RUN: -define-always-enabled-availability-domain AlwaysEnabledDomain \
45
// RUN: -define-disabled-availability-domain DisabledDomain \
56
// RUN: -define-dynamic-availability-domain DynamicDomain
67

@@ -11,9 +12,15 @@ func alwaysAvailable() { }
1112
@available(EnabledDomain)
1213
func availableInEnabledDomain() { }
1314

15+
@available(AlwaysEnabledDomain)
16+
func availableInAlwaysEnabledDomain() { }
17+
1418
@available(EnabledDomain, unavailable)
1519
func unavailableInEnabledDomain() { } // expected-note * {{'unavailableInEnabledDomain()' has been explicitly marked unavailable here}}
1620

21+
@available(AlwaysEnabledDomain, unavailable)
22+
func unavailableInAlwaysEnabledDomain() { } // expected-note * {{'unavailableInAlwaysEnabledDomain()' has been explicitly marked unavailable here}}
23+
1724
@available(DisabledDomain, unavailable)
1825
func unavailableInDisabledDomain() { } // expected-note * {{'unavailableInDisabledDomain()' has been explicitly marked unavailable here}}
1926

@@ -41,7 +48,9 @@ func testDeployment() { // expected-note 3 {{add '@available' attribute to enclo
4148
alwaysAvailable()
4249
availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}}
4350
// expected-note@-1 {{add 'if #available' version check}}
51+
availableInAlwaysEnabledDomain()
4452
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
53+
unavailableInAlwaysEnabledDomain() // expected-error {{'unavailableInAlwaysEnabledDomain()' is unavailable}}
4554
unavailableInDisabledDomain() // expected-error {{'unavailableInDisabledDomain()' is unavailable}}
4655
deprecatedInDynamicDomain() // expected-warning {{'deprecatedInDynamicDomain()' is deprecated: Use something else}}
4756
unavailableInDynamicDomain() // expected-error {{'unavailableInDynamicDomain()' is unavailable}}
@@ -53,10 +62,14 @@ func testDeployment() { // expected-note 3 {{add '@available' attribute to enclo
5362
availableAndUnavailableInEnabledDomain() // expected-error {{'availableAndUnavailableInEnabledDomain()' is unavailable}}
5463
}
5564

65+
// FIXME: [availability] Test @inlinable functions.
66+
5667
func testIfAvailable(_ truthy: Bool) { // expected-note 9 {{add '@available' attribute to enclosing global function}}
5768
if #available(EnabledDomain) { // expected-note {{enclosing scope here}}
5869
availableInEnabledDomain()
70+
availableInAlwaysEnabledDomain()
5971
unavailableInEnabledDomain() // expected-error {{'unavailableInEnabledDomain()' is unavailable}}
72+
unavailableInAlwaysEnabledDomain() // expected-error {{'unavailableInAlwaysEnabledDomain()' is unavailable}}
6073
availableInDynamicDomain() // expected-error {{'availableInDynamicDomain()' is only available in DynamicDomain}}
6174
// expected-note@-1 {{add 'if #available' version check}}
6275
unavailableInDynamicDomain() // expected-error {{'unavailableInDynamicDomain()' is unavailable}}
@@ -133,6 +146,14 @@ func testIfAvailable(_ truthy: Bool) { // expected-note 9 {{add '@available' att
133146
if #unavailable(EnabledDomain), #available(DynamicDomain) {
134147
// expected-error@-1 {{#available and #unavailable cannot be in the same statement}}
135148
}
149+
150+
if #available(AlwaysEnabledDomain) {
151+
availableInAlwaysEnabledDomain()
152+
unavailableInAlwaysEnabledDomain() // expected-error {{'unavailableInAlwaysEnabledDomain()' is unavailable}}
153+
} else {
154+
availableInAlwaysEnabledDomain()
155+
unavailableInAlwaysEnabledDomain()
156+
}
136157
}
137158

138159
func testWhileAvailable() { // expected-note {{add '@available' attribute to enclosing global function}}
@@ -207,13 +228,28 @@ func testEnabledDomainUnavailable() { // expected-note {{add '@available' attrib
207228
availableInUnknownDomain()
208229
}
209230

231+
@available(AlwaysEnabledDomain)
232+
func testAlwaysEnabledDomainAvailable() {
233+
availableInAlwaysEnabledDomain()
234+
unavailableInAlwaysEnabledDomain() // expected-error {{'unavailableInAlwaysEnabledDomain()' is unavailable}}
235+
}
236+
237+
@available(AlwaysEnabledDomain, unavailable)
238+
func testAlwaysEnabledDomainUnavailable() {
239+
availableInAlwaysEnabledDomain()
240+
unavailableInAlwaysEnabledDomain()
241+
}
242+
210243
@available(*, unavailable)
211244
func testUniversallyUnavailable() {
212245
alwaysAvailable()
213246
// FIXME: [availability] Diagnostic consistency: potentially unavailable declaration shouldn't be diagnosed
214247
// in contexts that are unavailable to broader domains
215248
availableInEnabledDomain() // expected-error {{'availableInEnabledDomain()' is only available in EnabledDomain}}
216249
// expected-note@-1 {{add 'if #available' version check}}
250+
unavailableInEnabledDomain()
251+
availableInAlwaysEnabledDomain()
252+
unavailableInAlwaysEnabledDomain()
217253
unavailableInDisabledDomain()
218254
deprecatedInDynamicDomain() // expected-warning {{'deprecatedInDynamicDomain()' is deprecated: Use something else}}
219255
availableInDynamicDomain() // expected-error {{'availableInDynamicDomain()' is only available in DynamicDomain}}

test/ClangImporter/Inputs/availability_custom_domains_other.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,26 @@ func availableInMediterranean() { }
99
func testOtherClangDecls() { // expected-note {{add '@available' attribute to enclosing global function}}
1010
available_in_baltic() // expected-error {{'available_in_baltic()' is only available in Baltic}}
1111
// expected-note@-1 {{add 'if #available' version check}}
12+
available_in_bering() // ok, Bering is always available
13+
unavailable_in_bering() // expected-error {{'unavailable_in_bering()' is unavailable}}
14+
}
15+
16+
@available(Baltic)
17+
func availableInBalticOther() {
18+
available_in_baltic()
19+
available_in_bering() // ok, Bering is always available
20+
unavailable_in_bering() // expected-error {{'unavailable_in_bering()' is unavailable}}
21+
}
22+
23+
@available(Bering)
24+
func availableInBering() { // expected-note {{add '@available' attribute to enclosing global function}}
25+
available_in_baltic() // expected-error {{'available_in_baltic()' is only available in Baltic}}
26+
// expected-note@-1 {{add 'if #available' version check}}
27+
available_in_bering()
28+
unavailable_in_bering() // expected-error {{'unavailable_in_bering()' is unavailable}}
29+
}
30+
31+
@available(Bering, unavailable)
32+
func unavailableInBering() {
33+
unavailable_in_bering()
1234
}

0 commit comments

Comments
 (0)