Skip to content

Commit 13c71c6

Browse files
authored
Merge pull request #40769 from etcwilde/ewilde/noasync-unavailability
SE-0340: `noasync` availability kind
2 parents 28e6822 + aa22e79 commit 13c71c6

21 files changed

+295
-31
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ _**Note:** This is in reverse chronological order, so newer entries are added to
55

66
## Swift 5.7
77

8+
* [SE-0340][]:
9+
10+
It is now possible to make declarations unavailable from use in asynchronous
11+
contexts with the `@available(*, noasync)` attribute.
12+
13+
This is to protect the consumers of an API against undefined behavior that can
14+
occur when the API uses thread-local storage, or encourages using thread-local
15+
storage, across suspension points, or protect developers against holding locks
16+
across suspension points which may lead to undefined behavior, priority
17+
inversions, or deadlocks.
18+
819
* [SE-0343][]:
920

1021
Top-level scripts support asynchronous calls.
@@ -9083,6 +9094,7 @@ Swift 1.0
90839094
[SE-0341]: <https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md>
90849095
[SE-0336]: <https://github.com/apple/swift-evolution/blob/main/proposals/0336-distributed-actor-isolation.md>
90859096
[SE-0343]: <https://github.com/apple/swift-evolution/blob/main/proposals/0343-top-level-concurrency.md>
9097+
[SE-0340]: <https://github.com/apple/swift-evolution/blob/main/proposals/0340-swift-noasync.md>
90869098

90879099
[SR-75]: <https://bugs.swift.org/browse/SR-75>
90889100
[SR-106]: <https://bugs.swift.org/browse/SR-106>

include/swift/AST/Attr.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,8 @@ enum class PlatformAgnosticAvailabilityKind {
614614
PackageDescriptionVersionSpecific,
615615
/// The declaration is unavailable for other reasons.
616616
Unavailable,
617+
/// The declaration is unavailable from asynchronous contexts
618+
NoAsync,
617619
};
618620

619621
/// Defines the @available attribute.
@@ -702,6 +704,9 @@ class AvailableAttr : public DeclAttribute {
702704
/// Whether this is an unconditionally deprecated entity.
703705
bool isUnconditionallyDeprecated() const;
704706

707+
/// Whether this is a noasync attribute.
708+
bool isNoAsync() const;
709+
705710
/// Returns the platform-agnostic availability.
706711
PlatformAgnosticAvailabilityKind getPlatformAgnosticAvailability() const {
707712
return PlatformAgnostic;
@@ -2261,6 +2266,11 @@ class DeclAttributes {
22612266
/// a declaration will be deprecated in the future, or null otherwise.
22622267
const AvailableAttr *getSoftDeprecated(const ASTContext &ctx) const;
22632268

2269+
/// Returns the first @available attribute that indicates
2270+
/// a declaration is unavailable from asynchronous contexts, or null
2271+
/// otherwise.
2272+
const AvailableAttr *getNoAsync(const ASTContext &ctx) const;
2273+
22642274
SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr));
22652275
void print(ASTPrinter &Printer, const PrintOptions &Options,
22662276
const Decl *D = nullptr) const;

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,8 @@ ERROR(attr_unsupported_on_target, none,
14971497
// availability
14981498
ERROR(attr_availability_platform,none,
14991499
"expected platform name or '*' for '%0' attribute", (StringRef))
1500-
ERROR(attr_availability_unavailable_deprecated,none,
1501-
"'%0' attribute cannot be both unconditionally 'unavailable' and "
1502-
"'deprecated'", (StringRef))
1500+
ERROR(attr_availability_multiple_kinds ,none,
1501+
"'%0' attribute cannot be both '%1' and '%2'", (StringRef, StringRef, StringRef))
15031502

15041503
WARNING(attr_availability_invalid_duplicate,none,
15051504
"'%0' argument has already been specified", (StringRef))

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4844,8 +4844,8 @@ ERROR(async_named_decl_must_be_available_from_async,none,
48444844
"asynchronous %0 %1 must be available from asynchronous contexts",
48454845
(DescriptiveDeclKind, DeclName))
48464846
ERROR(async_unavailable_decl,none,
4847-
"%0 %1 is unavailable from asynchronous contexts%select{|; %3}2",
4848-
(DescriptiveDeclKind, DeclBaseName, bool, StringRef))
4847+
"%0 %1 is unavailable from asynchronous contexts%select{|; %2}2",
4848+
(DescriptiveDeclKind, DeclBaseName, StringRef))
48494849

48504850
//------------------------------------------------------------------------------
48514851
// MARK: String Processing

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ struct PrintOptions {
301301
/// Whether to print generic requirements in a where clause.
302302
bool PrintGenericRequirements = true;
303303

304+
/// Suppress emitting @available(*, noasync)
305+
bool SuppressNoAsyncAvailabilityAttr = false;
306+
304307
/// How to print opaque return types.
305308
enum class OpaqueReturnTypePrintingMode {
306309
/// 'some P1 & P2'.

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ LANGUAGE_FEATURE(BuiltinAssumeAlignment, 0, "Builtin.assumeAlignment", true)
7676
SUPPRESSIBLE_LANGUAGE_FEATURE(UnsafeInheritExecutor, 0, "@_unsafeInheritExecutor", true)
7777
SUPPRESSIBLE_LANGUAGE_FEATURE(PrimaryAssociatedTypes, 0, "Primary associated types", true)
7878
SUPPRESSIBLE_LANGUAGE_FEATURE(UnavailableFromAsync, 0, "@_unavailableFromAsync", true)
79+
SUPPRESSIBLE_LANGUAGE_FEATURE(NoAsyncAvailability, 340, "@available(*, noasync)", true)
7980

8081
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
8182
#undef LANGUAGE_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,18 @@ suppressingFeatureUnavailableFromAsync(PrintOptions &options,
30153015
options.ExcludeAttrList.resize(originalExcludeAttrCount);
30163016
}
30173017

3018+
static bool usesFeatureNoAsyncAvailability(Decl *decl) {
3019+
return decl->getAttrs().getNoAsync(decl->getASTContext()) != nullptr;
3020+
}
3021+
3022+
static void
3023+
suppressingFeatureNoAsyncAvailability(PrintOptions &options,
3024+
llvm::function_ref<void()> action) {
3025+
llvm::SaveAndRestore<PrintOptions> orignalOptions(options);
3026+
options.SuppressNoAsyncAvailabilityAttr = true;
3027+
action();
3028+
}
3029+
30183030
/// Suppress the printing of a particular feature.
30193031
static void suppressingFeature(PrintOptions &options, Feature feature,
30203032
llvm::function_ref<void()> action) {

lib/AST/Attr.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ DeclAttributes::findMostSpecificActivePlatform(const ASTContext &ctx) const{
190190
continue;
191191

192192
// We have an attribute that is active for the platform, but
193-
// is it more specific than our curent best?
193+
// is it more specific than our current best?
194194
if (!bestAttr || inheritsAvailabilityFromPlatform(avAttr->Platform,
195195
bestAttr->Platform)) {
196196
bestAttr = avAttr;
@@ -356,6 +356,48 @@ DeclAttributes::getSoftDeprecated(const ASTContext &ctx) const {
356356
return conditional;
357357
}
358358

359+
const AvailableAttr *DeclAttributes::getNoAsync(const ASTContext &ctx) const {
360+
const AvailableAttr *bestAttr = nullptr;
361+
for (const DeclAttribute *attr : *this) {
362+
if (const AvailableAttr *avAttr = dyn_cast<AvailableAttr>(attr)) {
363+
if (avAttr->isInvalid())
364+
continue;
365+
366+
if (avAttr->getPlatformAgnosticAvailability() ==
367+
PlatformAgnosticAvailabilityKind::NoAsync) {
368+
// An API may only be unavailable on specific platforms.
369+
// If it doesn't have a platform associated with it, then it's
370+
// unavailable for all platforms, so we should include it. If it does
371+
// have a platform and we are not that platform, then it doesn't apply
372+
// to us.
373+
const bool isGoodForPlatform =
374+
(avAttr->hasPlatform() && avAttr->isActivePlatform(ctx)) ||
375+
!avAttr->hasPlatform();
376+
377+
if (!isGoodForPlatform)
378+
continue;
379+
380+
if (!bestAttr) {
381+
// If there is no best attr selected
382+
// and the attr either has an active platform, or doesn't have one at
383+
// all, select it.
384+
bestAttr = avAttr;
385+
} else if (bestAttr && avAttr->hasPlatform() &&
386+
bestAttr->hasPlatform() &&
387+
inheritsAvailabilityFromPlatform(avAttr->Platform,
388+
bestAttr->Platform)) {
389+
// if they both have a viable platform, use the better one
390+
bestAttr = avAttr;
391+
} else if (avAttr->hasPlatform() && !bestAttr->hasPlatform()) {
392+
// Use the one more specific
393+
bestAttr = avAttr;
394+
}
395+
}
396+
}
397+
}
398+
return bestAttr;
399+
}
400+
359401
void DeclAttributes::dump(const Decl *D) const {
360402
StreamPrinter P(llvm::errs());
361403
PrintOptions PO = PrintOptions::printDeclarations();
@@ -394,6 +436,7 @@ static bool isShortAvailable(const DeclAttribute *DA) {
394436
case PlatformAgnosticAvailabilityKind::Deprecated:
395437
case PlatformAgnosticAvailabilityKind::Unavailable:
396438
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
439+
case PlatformAgnosticAvailabilityKind::NoAsync:
397440
return false;
398441
case PlatformAgnosticAvailabilityKind::None:
399442
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
@@ -771,6 +814,8 @@ static void printAvailableAttr(const AvailableAttr *Attr, ASTPrinter &Printer,
771814
Printer << ", unavailable";
772815
else if (Attr->isUnconditionallyDeprecated())
773816
Printer << ", deprecated";
817+
else if (Attr->isNoAsync())
818+
Printer << ", noasync";
774819

775820
if (Attr->Introduced)
776821
Printer << ", introduced: " << Attr->Introduced.getValue().getAsString();
@@ -974,6 +1019,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
9741019

9751020
case DAK_Available: {
9761021
auto Attr = cast<AvailableAttr>(this);
1022+
if (Options.SuppressNoAsyncAvailabilityAttr && Attr->isNoAsync())
1023+
return false;
9771024
if (!Options.PrintSPIs && Attr->IsSPI) {
9781025
assert(Attr->hasPlatform());
9791026
assert(Attr->Introduced.hasValue());
@@ -1705,6 +1752,7 @@ bool AvailableAttr::isUnconditionallyUnavailable() const {
17051752
case PlatformAgnosticAvailabilityKind::Deprecated:
17061753
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
17071754
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
1755+
case PlatformAgnosticAvailabilityKind::NoAsync:
17081756
return false;
17091757

17101758
case PlatformAgnosticAvailabilityKind::Unavailable:
@@ -1722,6 +1770,7 @@ bool AvailableAttr::isUnconditionallyDeprecated() const {
17221770
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
17231771
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
17241772
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
1773+
case PlatformAgnosticAvailabilityKind::NoAsync:
17251774
return false;
17261775

17271776
case PlatformAgnosticAvailabilityKind::Deprecated:
@@ -1731,6 +1780,10 @@ bool AvailableAttr::isUnconditionallyDeprecated() const {
17311780
llvm_unreachable("Unhandled PlatformAgnosticAvailabilityKind in switch.");
17321781
}
17331782

1783+
bool AvailableAttr::isNoAsync() const {
1784+
return PlatformAgnostic == PlatformAgnosticAvailabilityKind::NoAsync;
1785+
}
1786+
17341787
llvm::VersionTuple AvailableAttr::getActiveVersion(const ASTContext &ctx) const {
17351788
if (isLanguageVersionSpecific()) {
17361789
return ctx.LangOpts.EffectiveLanguageVersion;

lib/AST/Decl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7533,9 +7533,10 @@ AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const {
75337533
// rename parameter, falling back to the first with a rename. Note that
75347534
// `getAttrs` is in reverse source order, so the last attribute is the
75357535
// first in source
7536-
if (!attr->Rename.empty() && (attr->Platform == PlatformKind::none ||
7537-
!avAttr))
7536+
if (!attr->Rename.empty() &&
7537+
(attr->Platform == PlatformKind::none || !avAttr) && !attr->isNoAsync()) {
75387538
avAttr = attr;
7539+
}
75397540
}
75407541

75417542
auto *renamedDecl = evaluateOrDefault(

lib/Parse/ParseDecl.cpp

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -327,23 +327,48 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
327327
++ParamIndex;
328328

329329
enum {
330-
IsMessage, IsRenamed,
331-
IsIntroduced, IsDeprecated, IsObsoleted,
330+
IsMessage,
331+
IsRenamed,
332+
IsIntroduced,
333+
IsDeprecated,
334+
IsObsoleted,
332335
IsUnavailable,
336+
IsNoAsync,
333337
IsInvalid
334338
} ArgumentKind = IsInvalid;
335-
339+
336340
if (Tok.is(tok::identifier)) {
337-
ArgumentKind =
338-
llvm::StringSwitch<decltype(ArgumentKind)>(ArgumentKindStr)
339-
.Case("message", IsMessage)
340-
.Case("renamed", IsRenamed)
341-
.Case("introduced", IsIntroduced)
342-
.Case("deprecated", IsDeprecated)
343-
.Case("obsoleted", IsObsoleted)
344-
.Case("unavailable", IsUnavailable)
345-
.Default(IsInvalid);
346-
}
341+
ArgumentKind = llvm::StringSwitch<decltype(ArgumentKind)>(ArgumentKindStr)
342+
.Case("message", IsMessage)
343+
.Case("renamed", IsRenamed)
344+
.Case("introduced", IsIntroduced)
345+
.Case("deprecated", IsDeprecated)
346+
.Case("obsoleted", IsObsoleted)
347+
.Case("unavailable", IsUnavailable)
348+
.Case("noasync", IsNoAsync)
349+
.Default(IsInvalid);
350+
}
351+
352+
auto platformAgnosticKindToStr = [](PlatformAgnosticAvailabilityKind kind) {
353+
switch (kind) {
354+
case PlatformAgnosticAvailabilityKind::None:
355+
return "none";
356+
case PlatformAgnosticAvailabilityKind::Deprecated:
357+
return "deprecated";
358+
case PlatformAgnosticAvailabilityKind::Unavailable:
359+
return "unavailable";
360+
case PlatformAgnosticAvailabilityKind::NoAsync:
361+
return "noasync";
362+
363+
// These are possible platform agnostic availability kinds.
364+
// I'm not sure what their spellings are at the moment, so I'm
365+
// crashing instead of handling them.
366+
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
367+
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
368+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
369+
llvm_unreachable("Unknown availability kind for parser");
370+
}
371+
};
347372

348373
if (ArgumentKind == IsInvalid) {
349374
diagnose(ArgumentLoc, diag::attr_availability_expected_option, AttrName)
@@ -419,8 +444,8 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
419444
case IsDeprecated:
420445
if (!findAttrValueDelimiter()) {
421446
if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) {
422-
diagnose(Tok, diag::attr_availability_unavailable_deprecated,
423-
AttrName);
447+
diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName,
448+
"deprecated", platformAgnosticKindToStr(PlatformAgnostic));
424449
}
425450

426451
PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated;
@@ -467,12 +492,21 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
467492

468493
case IsUnavailable:
469494
if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) {
470-
diagnose(Tok, diag::attr_availability_unavailable_deprecated, AttrName);
495+
diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName,
496+
"unavailable", platformAgnosticKindToStr(PlatformAgnostic));
471497
}
472498

473499
PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable;
474500
break;
475501

502+
case IsNoAsync:
503+
if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) {
504+
diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName,
505+
"noasync", platformAgnosticKindToStr(PlatformAgnostic));
506+
}
507+
PlatformAgnostic = PlatformAgnosticAvailabilityKind::NoAsync;
508+
break;
509+
476510
case IsInvalid:
477511
llvm_unreachable("handled above");
478512
}

0 commit comments

Comments
 (0)