Skip to content

Commit aa0e3dc

Browse files
authored
Merge pull request #78993 from tshortli/parse-custom-availability-domains
AST/Sema: Parse custom availability domains
2 parents 78ecec7 + 36c6c7f commit aa0e3dc

13 files changed

+130
-11
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
namespace swift {
2828
class ASTContext;
29+
class DeclContext;
2930

3031
/// Represents a dimension of availability (e.g. macOS platform or Swift
3132
/// language mode).
@@ -142,7 +143,7 @@ class AvailabilityDomain final {
142143

143144
/// Returns the built-in availability domain identified by the given string.
144145
static std::optional<AvailabilityDomain>
145-
builtinDomainForString(StringRef string);
146+
builtinDomainForString(StringRef string, const DeclContext *declContext);
146147

147148
Kind getKind() const {
148149
if (auto inlineDomain = getInlineDomain())

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ABIAttribute, true)
447447
/// calling context.
448448
EXPERIMENTAL_FEATURE(NonIsolatedAsyncInheritsIsolationFromContext, false)
449449

450+
/// Allow custom availability domains to be defined and referenced.
451+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(CustomAvailability, true)
452+
450453
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
451454
#undef EXPERIMENTAL_FEATURE
452455
#undef UPCOMING_FEATURE

include/swift/Frontend/FrontendOptions.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,18 @@ class FrontendOptions {
573573
/// All block list configuration files to be honored in this compilation.
574574
std::vector<std::string> BlocklistConfigFilePaths;
575575

576+
struct CustomAvailabilityDomains {
577+
/// Domains defined with `-define-enabled-availability-domain=`.
578+
llvm::SmallVector<std::string> EnabledDomains;
579+
/// Domains defined with `-define-disabled-availability-domain=`.
580+
llvm::SmallVector<std::string> DisabledDomains;
581+
/// Domains defined with `-define-dynamic-availability-domain=`.
582+
llvm::SmallVector<std::string> DynamicDomains;
583+
};
584+
585+
/// The collection of AvailabilityDomain definitions specified as arguments.
586+
CustomAvailabilityDomains AvailabilityDomains;
587+
576588
private:
577589
static bool canActionEmitDependencies(ActionType);
578590
static bool canActionEmitReferenceDependencies(ActionType);

include/swift/Option/Options.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,21 @@ def unavailable_decl_optimization_EQ : Joined<["-"], "unavailable-decl-optimizat
561561
"value may be 'none' (no optimization) or 'complete' (code is not "
562562
"generated at all unavailable declarations)">;
563563

564+
def define_enabled_availability_domain : Separate<["-"], "define-enabled-availability-domain">,
565+
Flags<[HelpHidden, FrontendOption, NoInteractiveOption, ModuleInterfaceOptionIgnorable]>,
566+
HelpText<"Defines a custom availability domain that is available at compile time">,
567+
MetaVarName<"<domain>">;
568+
569+
def define_disabled_availability_domain : Separate<["-"], "define-disabled-availability-domain">,
570+
Flags<[HelpHidden, FrontendOption, NoInteractiveOption, ModuleInterfaceOptionIgnorable]>,
571+
HelpText<"Defines a custom availability domain that is unavailable at compile time">,
572+
MetaVarName<"<domain>">;
573+
574+
def define_dynamic_availability_domain : Separate<["-"], "define-dynamic-availability-domain">,
575+
Flags<[HelpHidden, FrontendOption, NoInteractiveOption, ModuleInterfaceOptionIgnorable]>,
576+
HelpText<"Defines a custom availability domain that can be enabled or disabled at runtime">,
577+
MetaVarName<"<domain>">;
578+
564579
def experimental_package_bypass_resilience : Flag<["-"], "experimental-package-bypass-resilience">,
565580
Flags<[FrontendOption]>,
566581
HelpText<"Deprecated; has no effect">;

lib/AST/ASTPrinter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,6 +3239,14 @@ suppressingFeatureAddressableTypes(PrintOptions &options,
32393239
action();
32403240
}
32413241

3242+
static void
3243+
suppressingFeatureCustomAvailability(PrintOptions &options,
3244+
llvm::function_ref<void()> action) {
3245+
// FIXME: [availability] Save and restore a bit controlling whether
3246+
// @available attributes for custom domains are printed.
3247+
action();
3248+
}
3249+
32423250
/// Suppress the printing of a particular feature.
32433251
static void suppressingFeature(PrintOptions &options, Feature feature,
32443252
llvm::function_ref<void()> action) {

lib/AST/AvailabilityDomain.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
using namespace swift;
1919

2020
std::optional<AvailabilityDomain>
21-
AvailabilityDomain::builtinDomainForString(StringRef string) {
21+
AvailabilityDomain::builtinDomainForString(StringRef string,
22+
const DeclContext *declContext) {
23+
// This parameter is used in downstream forks, do not remove.
24+
(void)declContext;
25+
2226
auto domain = llvm::StringSwitch<std::optional<AvailabilityDomain>>(string)
2327
.Case("*", AvailabilityDomain::forUniversal())
2428
.Case("swift", AvailabilityDomain::forSwiftLanguage())

lib/AST/FeatureSet.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,12 @@ static bool usesFeatureCoroutineAccessors(Decl *decl) {
407407
}
408408
}
409409

410+
static bool usesFeatureCustomAvailability(Decl *decl) {
411+
// FIXME: [availability] Check whether @available attributes for custom
412+
// domains are attached to the decl.
413+
return false;
414+
}
415+
410416
// ----------------------------------------------------------------------------
411417
// MARK: - FeatureSet
412418
// ----------------------------------------------------------------------------

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ bool ArgsToFrontendOptionsConverter::convert(
419419
}
420420

421421
Opts.DisableSandbox = Args.hasArg(OPT_disable_sandbox);
422+
423+
if (computeAvailabilityDomains())
424+
return true;
425+
422426
return false;
423427
}
424428

@@ -548,6 +552,39 @@ void ArgsToFrontendOptionsConverter::computeDumpScopeMapLocations() {
548552
Diags.diagnose(SourceLoc(), diag::error_no_source_location_scope_map);
549553
}
550554

555+
bool ArgsToFrontendOptionsConverter::computeAvailabilityDomains() {
556+
using namespace options;
557+
558+
bool hadError = false;
559+
llvm::SmallSet<std::string, 4> seenDomains;
560+
561+
for (const Arg *A :
562+
Args.filtered_reverse(OPT_define_enabled_availability_domain,
563+
OPT_define_disabled_availability_domain,
564+
OPT_define_dynamic_availability_domain)) {
565+
std::string domain = A->getValue();
566+
if (!seenDomains.insert(domain).second)
567+
continue;
568+
569+
if (!Lexer::isIdentifier(domain)) {
570+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
571+
A->getAsString(Args), A->getValue());
572+
hadError = true;
573+
continue;
574+
}
575+
576+
auto &option = A->getOption();
577+
if (option.matches(OPT_define_enabled_availability_domain))
578+
Opts.AvailabilityDomains.EnabledDomains.emplace_back(domain);
579+
else if (option.matches(OPT_define_disabled_availability_domain))
580+
Opts.AvailabilityDomains.DisabledDomains.emplace_back(domain);
581+
else if (option.matches(OPT_define_dynamic_availability_domain))
582+
Opts.AvailabilityDomains.DynamicDomains.emplace_back(domain);
583+
}
584+
585+
return hadError;
586+
}
587+
551588
FrontendOptions::ActionType
552589
ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) {
553590
using namespace options;

lib/Frontend/ArgsToFrontendOptionsConverter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class ArgsToFrontendOptionsConverter {
4848
void computePlaygroundOptions();
4949
void computePrintStatsOptions();
5050
void computeTBDOptions();
51+
bool computeAvailabilityDomains();
5152

5253
bool setUpImmediateArgs();
5354

lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
537537
}
538538
}
539539

540-
if (!AnyAnnotations) {
540+
if (!AnyAnnotations &&
541+
!Context.LangOpts.hasFeature(Feature::CustomAvailability)) {
541542
diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName,
542543
/*isDeclModifier*/ false);
543544
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8271,7 +8271,6 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
82718271
auto &diags = decl->getASTContext().Diags;
82728272
auto attrLoc = attr->getLocation();
82738273
auto attrName = attr->getAttrName();
8274-
auto attrKind = attr->getKind();
82758274
auto domainLoc = attr->getDomainLoc();
82768275
auto introducedVersion = attr->getRawIntroduced();
82778276
auto deprecatedVersion = attr->getRawDeprecated();
@@ -8285,7 +8284,8 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
82858284

82868285
// Attempt to resolve the domain specified for the attribute and diagnose
82878286
// if no domain is found.
8288-
domain = AvailabilityDomain::builtinDomainForString(*string);
8287+
auto declContext = decl->getInnermostDeclContext();
8288+
domain = AvailabilityDomain::builtinDomainForString(*string, declContext);
82898289
if (!domain) {
82908290
if (auto suggestion = closestCorrectedPlatformString(*string)) {
82918291
diags
@@ -8305,7 +8305,7 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
83058305
auto domainName = domain->getNameForAttributePrinting();
83068306

83078307
if (domain->isSwiftLanguage() || domain->isPackageDescription()) {
8308-
switch (attrKind) {
8308+
switch (attr->getKind()) {
83098309
case AvailableAttr::Kind::Deprecated:
83108310
diags.diagnose(attrLoc,
83118311
diag::attr_availability_expected_deprecated_version,
@@ -8320,7 +8320,8 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
83208320
case AvailableAttr::Kind::NoAsync:
83218321
diags.diagnose(attrLoc, diag::attr_availability_cannot_be_used_for_domain,
83228322
"noasync", attrName, domainName);
8323-
break;
8323+
return std::nullopt;
8324+
83248325
case AvailableAttr::Kind::Default:
83258326
break;
83268327
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-typecheck-verify-swift \
2+
// RUN: -enable-experimental-feature CustomAvailability \
3+
// RUN: -define-enabled-availability-domain EnabledDomain \
4+
// RUN: -define-enabled-availability-domain RedefinedDomain \
5+
// RUN: -define-disabled-availability-domain DisabledDomain \
6+
// RUN: -define-dynamic-availability-domain DynamicDomain \
7+
// RUN: -define-disabled-availability-domain RedefinedDomain
8+
9+
// REQUIRES: swift_feature_CustomAvailability
10+
11+
@available(EnabledDomain) // expected-warning {{unknown platform 'EnabledDomain' for attribute 'available'}}
12+
func availableInEnabledDomain() { }
13+
14+
@available(DisabledDomain, unavailable) // expected-warning {{unknown platform 'DisabledDomain' for attribute 'available'}}
15+
func availableInDisabledDomain() { }
16+
17+
@available(RedefinedDomain, deprecated, message: "Use something else") // expected-warning {{unknown platform 'RedefinedDomain' for attribute 'available'}}
18+
func availableInRedefinedDomain() { }
19+
20+
@available(DynamicDomain) // expected-warning {{unknown platform 'DynamicDomain' for attribute 'available'}}
21+
func availableInDynamicDomain() { }
22+
23+
@available(UnknownDomain) // expected-warning {{unknown platform 'UnknownDomain' for attribute 'available'}}
24+
func availableInUnknownDomain() { }
25+
26+
func test() {
27+
availableInEnabledDomain()
28+
availableInDisabledDomain()
29+
availableInRedefinedDomain()
30+
availableInDynamicDomain()
31+
availableInUnknownDomain()
32+
}

test/attr/attr_availability_noasync.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ func asyncReplacement() async -> Int { }
1818
@available(*, noasync, renamed: "IOActor.readString()")
1919
func readStringFromIO() -> String {}
2020

21-
// expected-warning@+2 {{'noasync' cannot be used in 'available' attribute for platform 'swift'}}
22-
// expected-warning@+1 {{expected 'introduced', 'deprecated', or 'obsoleted' in 'available' attribute for platform 'swift'}}
21+
// expected-warning@+1 {{'noasync' cannot be used in 'available' attribute for platform 'swift'}}
2322
@available(swift, noasync)
2423
func swiftNoAsync() { }
2524

26-
// expected-warning@+2 {{'noasync' cannot be used in 'available' attribute for platform '_PackageDescription'}}
27-
// expected-warning@+1 {{expected 'introduced', 'deprecated', or 'obsoleted' in 'available' attribute for platform '_PackageDescription'}}
25+
// expected-warning@+1 {{'noasync' cannot be used in 'available' attribute for platform '_PackageDescription'}}
2826
@available(_PackageDescription, noasync)
2927
func packageDescriptionNoAsync() { }
3028

0 commit comments

Comments
 (0)