Skip to content

Commit 6512aa1

Browse files
committed
AST: Introduce and use ValueDecl::isAsync
1 parent 487d3b4 commit 6512aa1

File tree

10 files changed

+68
-79
lines changed

10 files changed

+68
-79
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3197,6 +3197,11 @@ class ValueDecl : public Decl {
31973197
/// Is this declaration marked with 'dynamic'?
31983198
bool isDynamic() const;
31993199

3200+
/// Returns whether accesses to this declaration are asynchronous.
3201+
/// If the declaration is neither `AbstractFunctionDecl` nor
3202+
/// `AbstractStorageDecl`, returns `false`.
3203+
bool isAsync() const;
3204+
32003205
private:
32013206
bool isObjCDynamic() const {
32023207
return isObjC() && isDynamic();

lib/AST/Decl.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4180,6 +4180,24 @@ bool ValueDecl::isDynamic() const {
41804180
getAttrs().hasAttribute<DynamicAttr>());
41814181
}
41824182

4183+
bool ValueDecl::isAsync() const {
4184+
if (auto *function = dyn_cast<AbstractFunctionDecl>(this)) {
4185+
return function->hasAsync();
4186+
}
4187+
4188+
// Async storage declarations must be get-only. Don't consider it async
4189+
// otherwise, even if it has an async getter.
4190+
if (auto *storage = dyn_cast<AbstractStorageDecl>(this)) {
4191+
if (storage->getAllAccessors().size() == 1) {
4192+
if (auto *getter = storage->getAccessor(AccessorKind::Get)) {
4193+
return getter->hasAsync();
4194+
}
4195+
}
4196+
}
4197+
4198+
return false;
4199+
}
4200+
41834201
bool ValueDecl::isObjCDynamicInGenericClass() const {
41844202
if (!isObjCDynamic())
41854203
return false;

lib/AST/DeclContext.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,15 +1615,10 @@ bool DeclContext::isAsyncContext() const {
16151615
return getParent()->isAsyncContext();
16161616
case DeclContextKind::AbstractClosureExpr:
16171617
return cast<AbstractClosureExpr>(this)->isBodyAsync();
1618-
case DeclContextKind::AbstractFunctionDecl: {
1619-
const AbstractFunctionDecl *function = cast<AbstractFunctionDecl>(this);
1620-
return function->hasAsync();
1621-
}
1622-
case DeclContextKind::SubscriptDecl: {
1623-
AccessorDecl *getter =
1624-
cast<SubscriptDecl>(this)->getAccessor(AccessorKind::Get);
1625-
return getter != nullptr && getter->hasAsync();
1626-
}
1618+
case DeclContextKind::AbstractFunctionDecl:
1619+
return cast<AbstractFunctionDecl>(this)->hasAsync();
1620+
case DeclContextKind::SubscriptDecl:
1621+
return cast<SubscriptDecl>(this)->isAsync();
16271622
}
16281623
llvm_unreachable("Unhandled DeclContextKind switch");
16291624
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7607,7 +7607,7 @@ void AttributeChecker::visitKnownToBeLocalAttr(KnownToBeLocalAttr *attr) {
76077607

76087608
void AttributeChecker::visitSendableAttr(SendableAttr *attr) {
76097609
if ((isa<AbstractFunctionDecl>(D) || isa<AbstractStorageDecl>(D)) &&
7610-
!isAsyncDecl(cast<ValueDecl>(D))) {
7610+
!cast<ValueDecl>(D)->isAsync()) {
76117611
auto value = cast<ValueDecl>(D);
76127612
ActorIsolation isolation = getActorIsolation(value);
76137613
if (isolation.isActorIsolated()) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ static bool isAsyncCall(
699699
if (lookup->isImplicitlyAsync())
700700
return true;
701701

702-
return isAsyncDecl(lookup->getDecl());
702+
return lookup->getDecl().getDecl()->isAsync();
703703
}
704704

705705
/// Determine whether we should diagnose data races within the current context.
@@ -2042,24 +2042,6 @@ static ActorIsolation getInnermostIsolatedContext(
20422042
}
20432043
}
20442044

2045-
/// Determine whether this declaration is always accessed asynchronously.
2046-
bool swift::isAsyncDecl(ConcreteDeclRef declRef) {
2047-
auto decl = declRef.getDecl();
2048-
2049-
// An async function is asynchronously accessed.
2050-
if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
2051-
return func->hasAsync();
2052-
2053-
// A computed property or subscript that has an 'async' getter
2054-
// is asynchronously accessed.
2055-
if (auto storageDecl = dyn_cast<AbstractStorageDecl>(decl)) {
2056-
if (auto effectfulGetter = storageDecl->getEffectfulGetAccessor())
2057-
return effectfulGetter->hasAsync();
2058-
}
2059-
2060-
return false;
2061-
}
2062-
20632045
AbstractFunctionDecl *swift::enclosingUnsafeInheritsExecutor(
20642046
const DeclContext *dc) {
20652047
for (; dc; dc = dc->getParent()) {
@@ -4443,7 +4425,7 @@ namespace {
44434425
// Make sure isolated conformances are formed in the right context.
44444426
checkIsolatedConformancesInContext(declRef, loc, getDeclContext());
44454427

4446-
auto decl = declRef.getDecl();
4428+
auto *const decl = declRef.getDecl();
44474429

44484430
// If this declaration is a callee from the enclosing application,
44494431
// it's already been checked via the call.
@@ -4500,7 +4482,7 @@ namespace {
45004482

45014483
// An escaping partial application of something that is part of
45024484
// the actor's isolated state is never permitted.
4503-
if (partialApply && partialApply->isEscaping && !isAsyncDecl(declRef)) {
4485+
if (partialApply && partialApply->isEscaping && !decl->isAsync()) {
45044486
ctx.Diags.diagnose(loc, diag::actor_isolated_partial_apply, decl);
45054487
return true;
45064488
}
@@ -5612,9 +5594,9 @@ static OverrideIsolationResult validOverrideIsolation(
56125594
// It's okay to enter the actor when the overridden declaration is
56135595
// asynchronous (because it will do the switch) or is accessible from
56145596
// anywhere.
5615-
if (isAsyncDecl(overridden) ||
5616-
isAccessibleAcrossActors(
5617-
overridden, refResult.isolation, declContext)) {
5597+
if (overridden->isAsync() ||
5598+
isAccessibleAcrossActors(overridden, refResult.isolation,
5599+
declContext)) {
56185600
return OverrideIsolationResult::Sendable;
56195601
}
56205602

@@ -7713,13 +7695,15 @@ ActorReferenceResult ActorReferenceResult::forReference(
77137695
std::optional<ActorIsolation> knownContextIsolation,
77147696
llvm::function_ref<ActorIsolation(AbstractClosureExpr *)>
77157697
getClosureActorIsolation) {
7698+
auto *const decl = declRef.getDecl();
7699+
77167700
// If not provided, compute the isolation of the declaration, adjusted
77177701
// for references.
77187702
ActorIsolation declIsolation = ActorIsolation::forUnspecified();
77197703
if (knownDeclIsolation) {
77207704
declIsolation = *knownDeclIsolation;
77217705
} else {
7722-
declIsolation = getActorIsolationForReference(declRef.getDecl(), fromDC);
7706+
declIsolation = getActorIsolationForReference(decl, fromDC);
77237707
if (declIsolation.requiresSubstitution())
77247708
declIsolation = declIsolation.subst(declRef.getSubstitutions());
77257709
}
@@ -7731,7 +7715,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
77317715
// FIXME: Actor constructors are modeled as isolated to the actor
77327716
// so that Sendable checking is applied to their arguments, but the
77337717
// call itself does not hop to another executor.
7734-
if (auto ctor = dyn_cast<ConstructorDecl>(declRef.getDecl())) {
7718+
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
77357719
if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) {
77367720
if (nominal->isAnyActor())
77377721
options |= Flags::OnlyArgsCrossIsolation;
@@ -7740,7 +7724,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
77407724

77417725
// If the entity we are referencing is not a value, we're in the same
77427726
// concurrency domain.
7743-
if (isNonValueReference(declRef.getDecl()))
7727+
if (isNonValueReference(decl))
77447728
return forSameConcurrencyDomain(declIsolation, options);
77457729

77467730
// Compute the isolation of the context, if not provided.
@@ -7757,9 +7741,10 @@ ActorReferenceResult ActorReferenceResult::forReference(
77577741
if (!declIsolation.isActorIsolated()) {
77587742
// If the declaration is asynchronous and we are in an actor-isolated
77597743
// context (of any kind), then we exit the actor to the nonisolated context.
7760-
if (isAsyncDecl(declRef) && contextIsolation.isActorIsolated() &&
7761-
!declRef.getDecl()->getAttrs()
7762-
.hasAttribute<UnsafeInheritExecutorAttr>())
7744+
if (decl->isAsync() && contextIsolation.isActorIsolated() &&
7745+
!declRef.getDecl()
7746+
->getAttrs()
7747+
.hasAttribute<UnsafeInheritExecutorAttr>())
77637748
return forExitsActorToNonisolated(contextIsolation, options);
77647749

77657750
// Otherwise, we stay in the same concurrency domain, whether on an actor
@@ -7788,11 +7773,9 @@ ActorReferenceResult ActorReferenceResult::forReference(
77887773
if ((declIsolation.isActorIsolated() && contextIsolation.isGlobalActor()) ||
77897774
declIsolation.isGlobalActor()) {
77907775
auto *init = dyn_cast<ConstructorDecl>(fromDC);
7791-
auto *decl = declRef.getDecl();
77927776
if (init && init->isDesignatedInit() && isStoredProperty(decl) &&
77937777
(!actorInstance || actorInstance->isSelf())) {
7794-
auto type =
7795-
fromDC->mapTypeIntoContext(declRef.getDecl()->getInterfaceType());
7778+
auto type = fromDC->mapTypeIntoContext(decl->getInterfaceType());
77967779
if (!type->isSendableType()) {
77977780
// Treat the decl isolation as 'preconcurrency' to downgrade violations
77987781
// to warnings, because violating Sendable here is accepted by the
@@ -7806,16 +7789,14 @@ ActorReferenceResult ActorReferenceResult::forReference(
78067789
// If there is an instance and it is checked by flow isolation, treat it
78077790
// as being in the same concurrency domain.
78087791
if (actorInstance &&
7809-
checkedByFlowIsolation(
7810-
fromDC, *actorInstance, declRef.getDecl(), declRefLoc, useKind))
7792+
checkedByFlowIsolation(fromDC, *actorInstance, decl, declRefLoc, useKind))
78117793
return forSameConcurrencyDomain(declIsolation, options);
78127794

78137795
// If we are delegating to another initializer, treat them as being in the
78147796
// same concurrency domain.
78157797
// FIXME: This has a lot of overlap with both the stored-property checks
78167798
// below and the flow-isolation checks above.
7817-
if (actorInstance && actorInstance->isSelf() &&
7818-
isa<ConstructorDecl>(declRef.getDecl()) &&
7799+
if (actorInstance && actorInstance->isSelf() && isa<ConstructorDecl>(decl) &&
78197800
isa<ConstructorDecl>(fromDC))
78207801
return forSameConcurrencyDomain(declIsolation, options);
78217802

@@ -7824,16 +7805,15 @@ ActorReferenceResult ActorReferenceResult::forReference(
78247805
// global-actor-qualified type, then we have problems if the stored property
78257806
// type is non-Sendable. Note that if we get here, the type must be Sendable.
78267807
if (actorInstance && actorInstance->isSelf() &&
7827-
isNonInheritedStorage(declRef.getDecl(), fromDC) &&
7828-
declIsolation.isGlobalActor() &&
7808+
isNonInheritedStorage(decl, fromDC) && declIsolation.isGlobalActor() &&
78297809
(isa<ConstructorDecl>(fromDC) || isa<DestructorDecl>(fromDC)))
78307810
return forSameConcurrencyDomain(declIsolation, options);
78317811

78327812
// At this point, we are accessing the target from outside the actor.
78337813
// First, check whether it is something that can be accessed directly,
78347814
// without any kind of promotion.
7835-
if (isAccessibleAcrossActors(
7836-
declRef.getDecl(), declIsolation, fromDC, options, actorInstance))
7815+
if (isAccessibleAcrossActors(decl, declIsolation, fromDC, options,
7816+
actorInstance))
78377817
return forEntersActor(declIsolation, options);
78387818

78397819
// This is a cross-actor reference.
@@ -7843,7 +7823,7 @@ ActorReferenceResult ActorReferenceResult::forReference(
78437823
options |= Flags::Preconcurrency;
78447824

78457825
// If the declaration isn't asynchronous, promote to async.
7846-
if (!isAsyncDecl(declRef))
7826+
if (!decl->isAsync())
78477827
options |= Flags::AsyncPromotion;
78487828

78497829
// If the declaration is isolated to a distributed actor and we are not

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,6 @@ ProtocolConformance *deriveImplicitSendableConformance(Evaluator &evaluator,
617617
/// returns a pointer to the declaration.
618618
const AbstractFunctionDecl *isActorInitOrDeInitContext(const DeclContext *dc);
619619

620-
/// Determine whether this declaration is always accessed asynchronously.
621-
bool isAsyncDecl(ConcreteDeclRef declRef);
622-
623620
/// Determine whether this declaration can throw errors.
624621
bool isThrowsDecl(ConcreteDeclRef declRef);
625622

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3307,19 +3307,6 @@ class ObjCImplementationChecker {
33073307
}
33083308

33093309
private:
3310-
static bool hasAsync(ValueDecl *member) {
3311-
if (!member)
3312-
return false;
3313-
3314-
if (auto func = dyn_cast<AbstractFunctionDecl>(member))
3315-
return func->hasAsync();
3316-
3317-
if (auto storage = dyn_cast<AbstractStorageDecl>(member))
3318-
return hasAsync(storage->getEffectfulGetAccessor());
3319-
3320-
return false;
3321-
}
3322-
33233310
static ValueDecl *getAsyncAlternative(ValueDecl *req) {
33243311
if (auto func = dyn_cast<AbstractFunctionDecl>(req)) {
33253312
auto asyncFunc = func->getAsyncAlternative();
@@ -3394,7 +3381,7 @@ class ObjCImplementationChecker {
33943381

33953382
// Skip async versions of members. We'll match against the completion
33963383
// handler versions, hopping over to `getAsyncAlternative()` if needed.
3397-
if (hasAsync(VD))
3384+
if (VD->isAsync())
33983385
return;
33993386

34003387
auto inserted = unmatchedRequirements.insert(VD);
@@ -3859,7 +3846,7 @@ class ObjCImplementationChecker {
38593846
ObjCSelector explicitObjCName) const {
38603847
// If the candidate we're considering is async, see if the requirement has
38613848
// an async alternate and try to match against that instead.
3862-
if (hasAsync(cand))
3849+
if (cand->isAsync())
38633850
if (auto asyncAltReq = getAsyncAlternative(req))
38643851
return matchesImpl(asyncAltReq, cand, explicitObjCName);
38653852

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,7 +3363,7 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
33633363
// Witnessing `async` requirement with an isolated synchronous
33643364
// declaration is done via async witness thunk which requires
33653365
// a hop to the expected concurrency domain.
3366-
if (isAsyncDecl(requirement) && !isAsyncDecl(witness))
3366+
if (requirement->isAsync() && !witness->isAsync())
33673367
return refResult.isolation;
33683368

33693369
// Otherwise, we're done.
@@ -3410,8 +3410,7 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
34103410
// FIXME: feels like ActorReferenceResult should be reporting this back to
34113411
// us somehow.
34123412
// To enter the actor, we always need the requirement to be `async`.
3413-
if (!sameConcurrencyDomain &&
3414-
!isAsyncDecl(requirement) &&
3413+
if (!sameConcurrencyDomain && !requirement->isAsync() &&
34153414
!isAccessibleAcrossActors(witness, refResult.isolation, DC))
34163415
missingOptions |= MissingFlags::RequirementAsync;
34173416

@@ -3502,7 +3501,7 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement,
35023501
return std::nullopt;
35033502

35043503
if (refResult.isolation.isActorIsolated()) {
3505-
if (isAsyncDecl(requirement) && !isAsyncDecl(witness))
3504+
if (requirement->isAsync() && !witness->isAsync())
35063505
return refResult.isolation;
35073506

35083507
// Always treat `@preconcurrency` witnesses as isolated.

test/attr/attr_availability_noasync.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ func asyncFunc() async {
5454
func unavailableAsyncFunc() async {
5555
}
5656

57+
do {
58+
struct S {
59+
@available(*, noasync)
60+
subscript(_: Int) -> Int {
61+
get async {}
62+
// expected-error@+1 {{'set' accessor is not allowed on property with 'get' accessor that is 'async' or 'throws'}}
63+
set {}
64+
}
65+
}
66+
}
67+
5768
@available(SwiftStdlib 5.5, *)
5869
protocol BadSyncable {
5970
// expected-error@+2{{asynchronous property 'isSyncd' must be available from asynchronous contexts}}

tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,8 @@ static bool initDocEntityInfo(const Decl *D,
451451
Info.IsUnavailable = D->isUnavailable();
452452
Info.IsDeprecated = D->isDeprecated();
453453
Info.IsOptional = D->getAttrs().hasAttribute<OptionalAttr>();
454-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
455-
Info.IsAsync = AFD->hasAsync();
456-
} else if (auto *Storage = dyn_cast<AbstractStorageDecl>(D)) {
457-
if (auto *Getter = Storage->getAccessor(AccessorKind::Get))
458-
Info.IsAsync = Getter->hasAsync();
454+
if (auto *valueDecl = dyn_cast<ValueDecl>(D)) {
455+
Info.IsAsync = valueDecl->isAsync();
459456
}
460457

461458
if (!IsRef) {

0 commit comments

Comments
 (0)