diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae2e3983d2ae..bb87a9a928f2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ _**Note:** This is in reverse chronological order, so newer entries are added to ## Swift 5.7 +* [SE-0340][]: + + It is now possible to make declarations unavailable from use in asynchronous + contexts with the `@available(*, noasync)` attribute. + + This is to protect the consumers of an API against undefined behavior that can + occur when the API uses thread-local storage, or encourages using thread-local + storage, across suspension points, or protect developers against holding locks + across suspension points which may lead to undefined behavior, priority + inversions, or deadlocks. + * [SE-0343][]: Top-level scripts support asynchronous calls. @@ -9083,6 +9094,7 @@ Swift 1.0 [SE-0341]: [SE-0336]: [SE-0343]: +[SE-0340]: [SR-75]: [SR-106]: diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index bd72feaac7c46..de292071ddff0 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -614,6 +614,8 @@ enum class PlatformAgnosticAvailabilityKind { PackageDescriptionVersionSpecific, /// The declaration is unavailable for other reasons. Unavailable, + /// The declaration is unavailable from asynchronous contexts + NoAsync, }; /// Defines the @available attribute. @@ -702,6 +704,9 @@ class AvailableAttr : public DeclAttribute { /// Whether this is an unconditionally deprecated entity. bool isUnconditionallyDeprecated() const; + /// Whether this is a noasync attribute. + bool isNoAsync() const; + /// Returns the platform-agnostic availability. PlatformAgnosticAvailabilityKind getPlatformAgnosticAvailability() const { return PlatformAgnostic; @@ -2261,6 +2266,11 @@ class DeclAttributes { /// a declaration will be deprecated in the future, or null otherwise. const AvailableAttr *getSoftDeprecated(const ASTContext &ctx) const; + /// Returns the first @available attribute that indicates + /// a declaration is unavailable from asynchronous contexts, or null + /// otherwise. + const AvailableAttr *getNoAsync(const ASTContext &ctx) const; + SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr)); void print(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D = nullptr) const; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 834854bbd1a97..661e21f6c5ab9 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1497,9 +1497,8 @@ ERROR(attr_unsupported_on_target, none, // availability ERROR(attr_availability_platform,none, "expected platform name or '*' for '%0' attribute", (StringRef)) -ERROR(attr_availability_unavailable_deprecated,none, - "'%0' attribute cannot be both unconditionally 'unavailable' and " - "'deprecated'", (StringRef)) +ERROR(attr_availability_multiple_kinds ,none, + "'%0' attribute cannot be both '%1' and '%2'", (StringRef, StringRef, StringRef)) WARNING(attr_availability_invalid_duplicate,none, "'%0' argument has already been specified", (StringRef)) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8943815552462..d369d84b048fe 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4848,8 +4848,8 @@ ERROR(async_named_decl_must_be_available_from_async,none, "asynchronous %0 %1 must be available from asynchronous contexts", (DescriptiveDeclKind, DeclName)) ERROR(async_unavailable_decl,none, - "%0 %1 is unavailable from asynchronous contexts%select{|; %3}2", - (DescriptiveDeclKind, DeclBaseName, bool, StringRef)) + "%0 %1 is unavailable from asynchronous contexts%select{|; %2}2", + (DescriptiveDeclKind, DeclBaseName, StringRef)) //------------------------------------------------------------------------------ // MARK: String Processing diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 3dd1c50889c7b..9419aef5312f9 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -301,6 +301,9 @@ struct PrintOptions { /// Whether to print generic requirements in a where clause. bool PrintGenericRequirements = true; + /// Suppress emitting @available(*, noasync) + bool SuppressNoAsyncAvailabilityAttr = false; + /// How to print opaque return types. enum class OpaqueReturnTypePrintingMode { /// 'some P1 & P2'. diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 77870c7343e2c..ab5560952d97e 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -76,6 +76,7 @@ LANGUAGE_FEATURE(BuiltinAssumeAlignment, 0, "Builtin.assumeAlignment", true) SUPPRESSIBLE_LANGUAGE_FEATURE(UnsafeInheritExecutor, 0, "@_unsafeInheritExecutor", true) SUPPRESSIBLE_LANGUAGE_FEATURE(PrimaryAssociatedTypes, 0, "Primary associated types", true) SUPPRESSIBLE_LANGUAGE_FEATURE(UnavailableFromAsync, 0, "@_unavailableFromAsync", true) +SUPPRESSIBLE_LANGUAGE_FEATURE(NoAsyncAvailability, 340, "@available(*, noasync)", true) #undef SUPPRESSIBLE_LANGUAGE_FEATURE #undef LANGUAGE_FEATURE diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 43108fc5ad6ca..000af787b4872 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3015,6 +3015,18 @@ suppressingFeatureUnavailableFromAsync(PrintOptions &options, options.ExcludeAttrList.resize(originalExcludeAttrCount); } +static bool usesFeatureNoAsyncAvailability(Decl *decl) { + return decl->getAttrs().getNoAsync(decl->getASTContext()) != nullptr; +} + +static void +suppressingFeatureNoAsyncAvailability(PrintOptions &options, + llvm::function_ref action) { + llvm::SaveAndRestore orignalOptions(options); + options.SuppressNoAsyncAvailabilityAttr = true; + action(); +} + /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8df6cc0215bad..12060e4eb9429 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -190,7 +190,7 @@ DeclAttributes::findMostSpecificActivePlatform(const ASTContext &ctx) const{ continue; // We have an attribute that is active for the platform, but - // is it more specific than our curent best? + // is it more specific than our current best? if (!bestAttr || inheritsAvailabilityFromPlatform(avAttr->Platform, bestAttr->Platform)) { bestAttr = avAttr; @@ -356,6 +356,48 @@ DeclAttributes::getSoftDeprecated(const ASTContext &ctx) const { return conditional; } +const AvailableAttr *DeclAttributes::getNoAsync(const ASTContext &ctx) const { + const AvailableAttr *bestAttr = nullptr; + for (const DeclAttribute *attr : *this) { + if (const AvailableAttr *avAttr = dyn_cast(attr)) { + if (avAttr->isInvalid()) + continue; + + if (avAttr->getPlatformAgnosticAvailability() == + PlatformAgnosticAvailabilityKind::NoAsync) { + // An API may only be unavailable on specific platforms. + // If it doesn't have a platform associated with it, then it's + // unavailable for all platforms, so we should include it. If it does + // have a platform and we are not that platform, then it doesn't apply + // to us. + const bool isGoodForPlatform = + (avAttr->hasPlatform() && avAttr->isActivePlatform(ctx)) || + !avAttr->hasPlatform(); + + if (!isGoodForPlatform) + continue; + + if (!bestAttr) { + // If there is no best attr selected + // and the attr either has an active platform, or doesn't have one at + // all, select it. + bestAttr = avAttr; + } else if (bestAttr && avAttr->hasPlatform() && + bestAttr->hasPlatform() && + inheritsAvailabilityFromPlatform(avAttr->Platform, + bestAttr->Platform)) { + // if they both have a viable platform, use the better one + bestAttr = avAttr; + } else if (avAttr->hasPlatform() && !bestAttr->hasPlatform()) { + // Use the one more specific + bestAttr = avAttr; + } + } + } + } + return bestAttr; +} + void DeclAttributes::dump(const Decl *D) const { StreamPrinter P(llvm::errs()); PrintOptions PO = PrintOptions::printDeclarations(); @@ -394,6 +436,7 @@ static bool isShortAvailable(const DeclAttribute *DA) { case PlatformAgnosticAvailabilityKind::Deprecated: case PlatformAgnosticAvailabilityKind::Unavailable: case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + case PlatformAgnosticAvailabilityKind::NoAsync: return false; case PlatformAgnosticAvailabilityKind::None: case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: @@ -771,6 +814,8 @@ static void printAvailableAttr(const AvailableAttr *Attr, ASTPrinter &Printer, Printer << ", unavailable"; else if (Attr->isUnconditionallyDeprecated()) Printer << ", deprecated"; + else if (Attr->isNoAsync()) + Printer << ", noasync"; if (Attr->Introduced) Printer << ", introduced: " << Attr->Introduced.getValue().getAsString(); @@ -974,6 +1019,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_Available: { auto Attr = cast(this); + if (Options.SuppressNoAsyncAvailabilityAttr && Attr->isNoAsync()) + return false; if (!Options.PrintSPIs && Attr->IsSPI) { assert(Attr->hasPlatform()); assert(Attr->Introduced.hasValue()); @@ -1705,6 +1752,7 @@ bool AvailableAttr::isUnconditionallyUnavailable() const { case PlatformAgnosticAvailabilityKind::Deprecated: case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + case PlatformAgnosticAvailabilityKind::NoAsync: return false; case PlatformAgnosticAvailabilityKind::Unavailable: @@ -1722,6 +1770,7 @@ bool AvailableAttr::isUnconditionallyDeprecated() const { case PlatformAgnosticAvailabilityKind::UnavailableInSwift: case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + case PlatformAgnosticAvailabilityKind::NoAsync: return false; case PlatformAgnosticAvailabilityKind::Deprecated: @@ -1731,6 +1780,10 @@ bool AvailableAttr::isUnconditionallyDeprecated() const { llvm_unreachable("Unhandled PlatformAgnosticAvailabilityKind in switch."); } +bool AvailableAttr::isNoAsync() const { + return PlatformAgnostic == PlatformAgnosticAvailabilityKind::NoAsync; +} + llvm::VersionTuple AvailableAttr::getActiveVersion(const ASTContext &ctx) const { if (isLanguageVersionSpecific()) { return ctx.LangOpts.EffectiveLanguageVersion; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2b11a2c2abc8c..973cb04234d2a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7533,9 +7533,10 @@ AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const { // rename parameter, falling back to the first with a rename. Note that // `getAttrs` is in reverse source order, so the last attribute is the // first in source - if (!attr->Rename.empty() && (attr->Platform == PlatformKind::none || - !avAttr)) + if (!attr->Rename.empty() && + (attr->Platform == PlatformKind::none || !avAttr) && !attr->isNoAsync()) { avAttr = attr; + } } auto *renamedDecl = evaluateOrDefault( diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3cebd21e9a156..2bf7d0b1a1c99 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -327,23 +327,48 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( ++ParamIndex; enum { - IsMessage, IsRenamed, - IsIntroduced, IsDeprecated, IsObsoleted, + IsMessage, + IsRenamed, + IsIntroduced, + IsDeprecated, + IsObsoleted, IsUnavailable, + IsNoAsync, IsInvalid } ArgumentKind = IsInvalid; - + if (Tok.is(tok::identifier)) { - ArgumentKind = - llvm::StringSwitch(ArgumentKindStr) - .Case("message", IsMessage) - .Case("renamed", IsRenamed) - .Case("introduced", IsIntroduced) - .Case("deprecated", IsDeprecated) - .Case("obsoleted", IsObsoleted) - .Case("unavailable", IsUnavailable) - .Default(IsInvalid); - } + ArgumentKind = llvm::StringSwitch(ArgumentKindStr) + .Case("message", IsMessage) + .Case("renamed", IsRenamed) + .Case("introduced", IsIntroduced) + .Case("deprecated", IsDeprecated) + .Case("obsoleted", IsObsoleted) + .Case("unavailable", IsUnavailable) + .Case("noasync", IsNoAsync) + .Default(IsInvalid); + } + + auto platformAgnosticKindToStr = [](PlatformAgnosticAvailabilityKind kind) { + switch (kind) { + case PlatformAgnosticAvailabilityKind::None: + return "none"; + case PlatformAgnosticAvailabilityKind::Deprecated: + return "deprecated"; + case PlatformAgnosticAvailabilityKind::Unavailable: + return "unavailable"; + case PlatformAgnosticAvailabilityKind::NoAsync: + return "noasync"; + + // These are possible platform agnostic availability kinds. + // I'm not sure what their spellings are at the moment, so I'm + // crashing instead of handling them. + case PlatformAgnosticAvailabilityKind::UnavailableInSwift: + case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific: + case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: + llvm_unreachable("Unknown availability kind for parser"); + } + }; if (ArgumentKind == IsInvalid) { diagnose(ArgumentLoc, diag::attr_availability_expected_option, AttrName) @@ -419,8 +444,8 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( case IsDeprecated: if (!findAttrValueDelimiter()) { if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { - diagnose(Tok, diag::attr_availability_unavailable_deprecated, - AttrName); + diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName, + "deprecated", platformAgnosticKindToStr(PlatformAgnostic)); } PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; @@ -467,12 +492,21 @@ ParserResult Parser::parseExtendedAvailabilitySpecList( case IsUnavailable: if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { - diagnose(Tok, diag::attr_availability_unavailable_deprecated, AttrName); + diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName, + "unavailable", platformAgnosticKindToStr(PlatformAgnostic)); } PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; break; + case IsNoAsync: + if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) { + diagnose(Tok, diag::attr_availability_multiple_kinds, AttrName, + "noasync", platformAgnosticKindToStr(PlatformAgnostic)); + } + PlatformAgnostic = PlatformAgnosticAvailabilityKind::NoAsync; + break; + case IsInvalid: llvm_unreachable("handled above"); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 215d6be9f4100..a265c6999563c 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1640,6 +1640,35 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { } } + if (attr->isNoAsync()) { + const DeclContext * dctx = dyn_cast(D); + bool isAsyncDeclContext = dctx && dctx->isAsyncContext(); + + if (const AbstractStorageDecl *decl = dyn_cast(D)) { + const AccessorDecl * accessor = decl->getEffectfulGetAccessor(); + isAsyncDeclContext |= accessor && accessor->isAsyncContext(); + } + + if (isAsyncDeclContext) { + if (const ValueDecl *vd = dyn_cast(D)) { + D->getASTContext().Diags.diagnose( + D->getLoc(), diag::async_named_decl_must_be_available_from_async, + D->getDescriptiveKind(), vd->getName()); + } else { + D->getASTContext().Diags.diagnose( + D->getLoc(), diag::async_decl_must_be_available_from_async, + D->getDescriptiveKind()); + } + } + + // deinit's may not be unavailable from async contexts + if (isa(D)) { + D->getASTContext().Diags.diagnose( + D->getLoc(), diag::invalid_decl_attribute, attr); + } + + } + if (!attr->hasPlatform() || !attr->isActivePlatform(Ctx) || !attr->Introduced.hasValue()) { return; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 7123ad0554e5e..ef1388d6b7824 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -2608,6 +2608,9 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc, case PlatformAgnosticAvailabilityKind::Deprecated: llvm_unreachable("shouldn't see deprecations in explicit unavailability"); + case PlatformAgnosticAvailabilityKind::NoAsync: + llvm_unreachable("shouldn't see noasync in explicit unavailability"); + case PlatformAgnosticAvailabilityKind::None: case PlatformAgnosticAvailabilityKind::Unavailable: if (attr->Platform != PlatformKind::none) { @@ -2772,6 +2775,9 @@ bool swift::diagnoseExplicitUnavailability( case PlatformAgnosticAvailabilityKind::Deprecated: llvm_unreachable("shouldn't see deprecations in explicit unavailability"); + case PlatformAgnosticAvailabilityKind::NoAsync: + llvm_unreachable("shouldn't see noasync with explicit unavailability"); + case PlatformAgnosticAvailabilityKind::None: case PlatformAgnosticAvailabilityKind::Unavailable: if (Attr->Platform != PlatformKind::none) { @@ -3296,16 +3302,32 @@ diagnoseDeclUnavailableFromAsync(const ValueDecl *D, SourceRange R, // If we are in a synchronous context, don't check it if (!Where.getDeclContext()->isAsyncContext()) return false; - if (!D->getAttrs().hasAttribute()) - return false; ASTContext &ctx = Where.getDeclContext()->getASTContext(); + if (const AvailableAttr *attr = D->getAttrs().getNoAsync(ctx)) { + SourceLoc diagLoc = call ? call->getLoc() : R.Start; + auto diag = ctx.Diags.diagnose(diagLoc, diag::async_unavailable_decl, + D->getDescriptiveKind(), D->getBaseName(), + attr->Message); + + if (!attr->Rename.empty()) { + fixItAvailableAttrRename(diag, R, D, attr, call); + } + return true; + } + + const bool hasUnavailableAttr = + D->getAttrs().hasAttribute(); + + if (!hasUnavailableAttr) + return false; + // @available(noasync) spelling const UnavailableFromAsyncAttr *attr = D->getAttrs().getAttribute(); SourceLoc diagLoc = call ? call->getLoc() : R.Start; ctx.Diags .diagnose(diagLoc, diag::async_unavailable_decl, D->getDescriptiveKind(), - D->getBaseName(), attr->hasMessage(), attr->Message) + D->getBaseName(), attr->Message) .warnUntilSwiftVersion(6); D->diagnose(diag::decl_declared_here, D->getName()); return true; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index a7f3c8d9345d9..7aae3ac33f3dd 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4361,6 +4361,7 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl &scratch, bool isImplicit; bool isUnavailable; bool isDeprecated; + bool isNoAsync; bool isPackageDescriptionVersionSpecific; bool isSPI; DEF_VER_TUPLE_PIECES(Introduced); @@ -4371,7 +4372,7 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl &scratch, // Decode the record, pulling the version tuple information. serialization::decls_block::AvailableDeclAttrLayout::readRecord( - scratch, isImplicit, isUnavailable, isDeprecated, + scratch, isImplicit, isUnavailable, isDeprecated, isNoAsync, isPackageDescriptionVersionSpecific, isSPI, LIST_VER_TUPLE_PIECES(Introduced), LIST_VER_TUPLE_PIECES(Deprecated), LIST_VER_TUPLE_PIECES(Obsoleted), platform, renameDeclID, messageSize, renameSize); @@ -4394,6 +4395,8 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl &scratch, platformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; else if (isDeprecated) platformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; + else if (isNoAsync) + platformAgnostic = PlatformAgnosticAvailabilityKind::NoAsync; else if (((PlatformKind)platform) == PlatformKind::none && (!Introduced.empty() || !Deprecated.empty() || !Obsoleted.empty())) platformAgnostic = diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index a8fe71727065e..09d4c70d3a0af 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 678; // remove shared_external linkage +const uint16_t SWIFTMODULE_VERSION_MINOR = 679; // NoAsync /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1916,6 +1916,7 @@ namespace decls_block { BCFixed<1>, // implicit flag BCFixed<1>, // is unconditionally unavailable? BCFixed<1>, // is unconditionally deprecated? + BCFixed<1>, // is unavailable from async? BCFixed<1>, // is this PackageDescription version-specific kind? BCFixed<1>, // is SPI? BC_AVAIL_TUPLE, // Introduced diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index c51f42a27d062..e257cbd69b36a 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2580,6 +2580,7 @@ class Serializer::DeclSerializer : public DeclVisitor { theAttr->isImplicit(), theAttr->isUnconditionallyUnavailable(), theAttr->isUnconditionallyDeprecated(), + theAttr->isNoAsync(), theAttr->isPackageDescriptionVersionSpecific(), theAttr->IsSPI, LIST_VER_TUPLE_PIECES(Introduced), diff --git a/lib/SymbolGraphGen/AvailabilityMixin.cpp b/lib/SymbolGraphGen/AvailabilityMixin.cpp index ba0789b29fedc..120a9049dc535 100644 --- a/lib/SymbolGraphGen/AvailabilityMixin.cpp +++ b/lib/SymbolGraphGen/AvailabilityMixin.cpp @@ -31,6 +31,7 @@ StringRef getDomain(const AvailableAttr &AvAttr) { case PlatformAgnosticAvailabilityKind::Deprecated: case PlatformAgnosticAvailabilityKind::Unavailable: case PlatformAgnosticAvailabilityKind::None: + case PlatformAgnosticAvailabilityKind::NoAsync: break; } diff --git a/test/Concurrency/Inputs/UnavailableFunction.swift b/test/Concurrency/Inputs/UnavailableFunction.swift index 0bbc8ee378f4b..db46c404df8f0 100644 --- a/test/Concurrency/Inputs/UnavailableFunction.swift +++ b/test/Concurrency/Inputs/UnavailableFunction.swift @@ -1,2 +1,5 @@ @_unavailableFromAsync public func unavailableFunction() { } + +@available(*, noasync) +public func noasyncFunction() { } diff --git a/test/Concurrency/unavailable_from_async.swift b/test/Concurrency/unavailable_from_async.swift index 325eec48b946b..73f23dba1fc96 100644 --- a/test/Concurrency/unavailable_from_async.swift +++ b/test/Concurrency/unavailable_from_async.swift @@ -64,11 +64,13 @@ func makeAsyncClosuresSynchronously(bop: inout Bop) -> (() async -> Void) { bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}} bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}} unavailableFunction() // expected-warning@:5{{'unavailableFunction' is unavailable from asynchronous contexts}} + noasyncFunction() // expected-error@:5{{'noasyncFunction' is unavailable from asynchronous contexts}} // Can use them from synchronous closures _ = { Bop() }() _ = { bop.foo() }() _ = { bop.muppet() }() + _ = { noasyncFunction() }() // Unavailable global function foo() // expected-warning{{'foo' is unavailable from asynchronous contexts}} @@ -87,6 +89,7 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF bop.foo() // expected-warning@:7{{'foo' is unavailable from asynchronous contexts}} bop.muppet() // expected-warning@:7{{'muppet' is unavailable from asynchronous contexts}} unavailableFunction() // expected-warning@:3{{'unavailableFunction' is unavailable from asynchronous contexts}} + noasyncFunction() // expected-error@:3{{'noasyncFunction' is unavailable from asynchronous contexts}} // Unavailable global function foo() // expected-warning{{'foo' is unavailable from asynchronous contexts}} @@ -101,6 +104,7 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF bop.foo() bop.muppet() unavailableFunction() + noasyncFunction() _ = { () async -> Void in // Check Unavailable things inside of a nested async closure @@ -109,6 +113,7 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF bop.muppet() // expected-warning@:11{{'muppet' is unavailable from asynchronous contexts}} _ = Bop() // expected-warning@:11{{'init' is unavailable from asynchronous contexts; Use Bop(a: Int) instead}} unavailableFunction() // expected-warning@:7{{'unavailableFunction' is unavailable from asynchronous contexts}} + noasyncFunction() // expected-error@:7{{'noasyncFunction' is unavailable from asynchronous contexts}} } } @@ -118,6 +123,7 @@ func asyncFunc() async { // expected-error{{asynchronous global function 'asyncF bop.foo() // expected-warning@:9{{'foo' is unavailable from asynchronous contexts}} bop.muppet() // expected-warning@:9{{'muppet' is unavailable from asynchronous contexts}} unavailableFunction() // expected-warning@:5{{'unavailableFunction' is unavailable from asynchronous contexts}} + noasyncFunction() // expected-error@:5{{'noasyncFunction' is unavailable from asynchronous contexts}} _ = { foo() diff --git a/test/ModuleInterface/features.swift b/test/ModuleInterface/features.swift index 7a56083bd48b3..206365aba309d 100644 --- a/test/ModuleInterface/features.swift +++ b/test/ModuleInterface/features.swift @@ -185,4 +185,13 @@ public func multipleSuppressible(value: T) async {} @_unavailableFromAsync(message: "Test") public func unavailableFromAsyncFunc() { } +// CHECK: #if compiler(>=5.3) && $NoAsyncAvailability +// CHECK-NEXT: @available(*, noasync, message: "Test") +// CHECK-NEXT: public func noAsyncFunc() +// CHECK-NEXT: #else +// CHECK-NEXT: public func noAsyncFunc() +// CHECK-NEXT: #endif +@available(*, noasync, message: "Test") +public func noAsyncFunc() { } + // CHECK-NOT: extension FeatureTest.MyActor : Swift.Sendable diff --git a/test/attr/attr_availability.swift b/test/attr/attr_availability.swift index 8579fa8104f00..e2588b77b2644 100644 --- a/test/attr/attr_availability.swift +++ b/test/attr/attr_availability.swift @@ -149,7 +149,7 @@ let _: Int @available(*, renamed: "a(:b:)") // expected-error{{'renamed' argument of 'available' attribute must be an operator, identifier, or full function name, optionally prefixed by a type name}} let _: Int -@available(*, deprecated, unavailable, message: "message") // expected-error{{'available' attribute cannot be both unconditionally 'unavailable' and 'deprecated'}} +@available(*, deprecated, unavailable, message: "message") // expected-error{{'available' attribute cannot be both 'unavailable' and 'deprecated'}} struct BadUnconditionalAvailability { }; @available(*, unavailable, message="oh no you don't") // expected-error {{'=' has been replaced with ':' in attribute arguments}} {{35-36=: }} @@ -1230,4 +1230,4 @@ struct UnavailableSubscripts { _ = self[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{13-14=.subscriptTo(}} {{19-20=)}} {{14-18=}} _ = x[to: 3] // expected-warning {{'subscript(to:)' is deprecated: renamed to 'subscriptTo(_:)'}} // expected-note {{use 'subscriptTo(_:)' instead}} {{10-11=.subscriptTo(}} {{16-17=)}} {{11-15=}} } -} \ No newline at end of file +} diff --git a/test/attr/attr_availability_noasync.swift b/test/attr/attr_availability_noasync.swift new file mode 100644 index 0000000000000..d2c7dc7444f4f --- /dev/null +++ b/test/attr/attr_availability_noasync.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +// REQUIRES: concurrency + + +@available(*, noasync) +func basicNoAsync() { } + +@available(*, noasync, message: "a message from the author") +func messageNoAsync() { } + +@available(*, noasync, renamed: "asyncReplacement()") +func renamedNoAsync(_ completion: @escaping (Int) -> Void) -> Void { } + +@available(macOS 11, iOS 13, watchOS 6, *) +func asyncReplacement() async -> Int { } + +@available(*, noasync, renamed: "IOActor.readString()") +func readStringFromIO() -> String {} + +@available(macOS 11, iOS 13, watchOS 6, *) +actor IOActor { + func readString() -> String { + return readStringFromIO() + } +} + +@available(macOS 11, iOS 13, watchOS 6, *) +func asyncFunc() async { + // expected-error@+1{{global function 'basicNoAsync' is unavailable from asynchronous contexts}} + basicNoAsync() + + // expected-error@+1{{global function 'messageNoAsync' is unavailable from asynchronous contexts; a message from the author}} + messageNoAsync() + + // expected-error@+1{{global function 'renamedNoAsync' is unavailable from asynchronous contexts}}{{5-19=asyncReplacement}} + renamedNoAsync() { _ in } + + // expected-error@+1{{global function 'readStringFromIO' is unavailable from asynchronous contexts}}{{13-29=IOActor.readString}} + let _ = readStringFromIO() +} + +// expected-error@+3{{asynchronous global function 'unavailableAsyncFunc()' must be available from asynchronous contexts}} +@available(macOS 11, iOS 13, watchOS 6, *) +@available(*, noasync) +func unavailableAsyncFunc() async { +} + +@available(macOS 11, iOS 13, watchOS 6, *) +protocol BadSyncable { + // expected-error@+2{{asynchronous property 'isSyncd' must be available from asynchronous contexts}} + @available(*, noasync) + var isSyncd: Bool { get async } + + // expected-error@+2{{asynchronous instance method 'sync' must be available from asynchronous contexts}} + @available(*, noasync) + func sync(_ str: String) async +} + +class TestClass { + // expected-error@+2{{'@available' attribute cannot be applied to this declaration}} + @available(*, noasync) + deinit { } +}