Skip to content

Commit 90c1fec

Browse files
authored
Merge pull request swiftlang#37002 from DougGregor/inherit-actor-context-attr
2 parents ed8b1b2 + 6928c3f commit 90c1fec

File tree

12 files changed

+176
-21
lines changed

12 files changed

+176
-21
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,11 @@ SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture,
652652
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
653653
115)
654654

655+
SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
656+
OnParam | UserInaccessible |
657+
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove,
658+
116)
659+
655660
#undef TYPE_ATTR
656661
#undef DECL_ATTR_ALIAS
657662
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/Expr.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,18 @@ class alignas(8) Expr {
292292
Kind : 2
293293
);
294294

295-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1,
295+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1,
296296
/// True if closure parameters were synthesized from anonymous closure
297297
/// variables.
298298
HasAnonymousClosureVars : 1,
299299

300300
/// True if "self" can be captured implicitly without requiring "self."
301301
/// on each member reference.
302-
ImplicitSelfCapture : 1
302+
ImplicitSelfCapture : 1,
303+
304+
/// True if this @Sendable async closure parameter should implicitly
305+
/// inherit the actor context from where it was formed.
306+
InheritActorContext : 1
303307
);
304308

305309
SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
@@ -3876,6 +3880,7 @@ class ClosureExpr : public AbstractClosureExpr {
38763880
setParameterList(params);
38773881
Bits.ClosureExpr.HasAnonymousClosureVars = false;
38783882
Bits.ClosureExpr.ImplicitSelfCapture = false;
3883+
Bits.ClosureExpr.InheritActorContext = false;
38793884
}
38803885

38813886
SourceRange getSourceRange() const;
@@ -3914,6 +3919,16 @@ class ClosureExpr : public AbstractClosureExpr {
39143919
Bits.ClosureExpr.ImplicitSelfCapture = value;
39153920
}
39163921

3922+
/// Whether this closure should implicitly inherit the actor context from
3923+
/// where it was formed. This only affects @Sendable async closures.
3924+
bool inheritsActorContext() const {
3925+
return Bits.ClosureExpr.InheritActorContext;
3926+
}
3927+
3928+
void setInheritsActorContext(bool value = true) {
3929+
Bits.ClosureExpr.InheritActorContext = value;
3930+
}
3931+
39173932
/// Determine whether this closure expression has an
39183933
/// explicitly-specified result type.
39193934
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

include/swift/AST/Types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3382,6 +3382,7 @@ struct ParameterListInfo {
33823382
SmallBitVector unsafeSendable;
33833383
SmallBitVector unsafeMainActor;
33843384
SmallBitVector implicitSelfCapture;
3385+
SmallBitVector inheritActorContext;
33853386

33863387
public:
33873388
ParameterListInfo() { }
@@ -3414,6 +3415,10 @@ struct ParameterListInfo {
34143415
/// 'self' to be implicit, without requiring "self.".
34153416
bool isImplicitSelfCapture(unsigned paramIdx) const;
34163417

3418+
/// Whether the given parameter is a closure that should inherit the
3419+
/// actor context from the context in which it was created.
3420+
bool inheritsActorContext(unsigned paramIdx) const;
3421+
34173422
/// Whether there is any contextual information set on this parameter list.
34183423
bool anyContextualInfo() const;
34193424

lib/AST/ASTDumper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2548,6 +2548,8 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
25482548
PrintWithColorRAII(OS, ClosureModifierColor) << " single-expression";
25492549
if (E->allowsImplicitSelfCapture())
25502550
PrintWithColorRAII(OS, ClosureModifierColor) << " implicit-self";
2551+
if (E->inheritsActorContext())
2552+
PrintWithColorRAII(OS, ClosureModifierColor) << " inherits-actor-context";
25512553

25522554
if (E->getParameters()) {
25532555
OS << '\n';

lib/AST/Type.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ ParameterListInfo::ParameterListInfo(
962962
unsafeSendable.resize(params.size());
963963
unsafeMainActor.resize(params.size());
964964
implicitSelfCapture.resize(params.size());
965+
inheritActorContext.resize(params.size());
965966

966967
// No parameter owner means no parameter list means no default arguments
967968
// - hand back the zeroed bitvector.
@@ -1025,6 +1026,10 @@ ParameterListInfo::ParameterListInfo(
10251026
if (param->getAttrs().hasAttribute<ImplicitSelfCaptureAttr>()) {
10261027
implicitSelfCapture.set(i);
10271028
}
1029+
1030+
if (param->getAttrs().hasAttribute<InheritActorContextAttr>()) {
1031+
inheritActorContext.set(i);
1032+
}
10281033
}
10291034
}
10301035

@@ -1061,9 +1066,15 @@ bool ParameterListInfo::isImplicitSelfCapture(unsigned paramIdx) const {
10611066
: false;
10621067
}
10631068

1069+
bool ParameterListInfo::inheritsActorContext(unsigned paramIdx) const {
1070+
return paramIdx < inheritActorContext.size()
1071+
? inheritActorContext[paramIdx]
1072+
: false;
1073+
}
1074+
10641075
bool ParameterListInfo::anyContextualInfo() const {
10651076
return unsafeSendable.any() || unsafeMainActor.any() ||
1066-
implicitSelfCapture.any();
1077+
implicitSelfCapture.any() || inheritActorContext.any();
10671078
}
10681079

10691080
/// Turn a param list into a symbolic and printable representation that does not

lib/Sema/CSApply.cpp

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5710,17 +5710,25 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
57105710

57115711
/// Apply the contextually Sendable flag to the given expression,
57125712
static void applyContextualClosureFlags(
5713-
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture) {
5713+
Expr *expr, bool sendable, bool forMainActor, bool implicitSelfCapture,
5714+
bool inheritActorContext) {
57145715
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
57155716
closure->setUnsafeConcurrent(sendable, forMainActor);
57165717
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
5718+
closure->setInheritsActorContext(inheritActorContext);
57175719
return;
57185720
}
57195721

57205722
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
57215723
applyContextualClosureFlags(
57225724
captureList->getClosureBody(), sendable, forMainActor,
5723-
implicitSelfCapture);
5725+
implicitSelfCapture, inheritActorContext);
5726+
}
5727+
5728+
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
5729+
applyContextualClosureFlags(
5730+
identity->getSubExpr(), sendable, forMainActor,
5731+
implicitSelfCapture, inheritActorContext);
57245732
}
57255733
}
57265734

@@ -5958,9 +5966,10 @@ Expr *ExprRewriter::coerceCallArguments(
59585966
bool isMainActor = paramInfo.isUnsafeMainActor(paramIdx) ||
59595967
(isUnsafeSendable && apply && isMainDispatchQueue(apply->getFn()));
59605968
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
5969+
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
59615970
applyContextualClosureFlags(
59625971
arg, isUnsafeSendable && contextUsesConcurrencyFeatures(dc),
5963-
isMainActor, isImplicitSelfCapture);
5972+
isMainActor, isImplicitSelfCapture, inheritsActorContext);
59645973

59655974
// If the types exactly match, this is easy.
59665975
auto paramType = param.getOldType();
@@ -6113,6 +6122,20 @@ static bool isClosureLiteralExpr(Expr *expr) {
61136122
return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
61146123
}
61156124

6125+
/// Whether we should propagate async down to a closure.
6126+
static bool shouldPropagateAsyncToClosure(Expr *expr) {
6127+
if (auto IE = dyn_cast<IdentityExpr>(expr))
6128+
return shouldPropagateAsyncToClosure(IE->getSubExpr());
6129+
6130+
if (auto CLE = dyn_cast<CaptureListExpr>(expr))
6131+
return shouldPropagateAsyncToClosure(CLE->getClosureBody());
6132+
6133+
if (auto CE = dyn_cast<ClosureExpr>(expr))
6134+
return CE->inheritsActorContext();
6135+
6136+
return false;
6137+
}
6138+
61166139
/// If the expression is an explicit closure expression (potentially wrapped in
61176140
/// IdentityExprs), change the type of the closure and identities to the
61186141
/// specified type and return true. Otherwise, return false with no effect.
@@ -6976,8 +6999,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69766999
}
69777000
}
69787001

6979-
// If we have a ClosureExpr, then we can safely propagate the 'concurrent'
6980-
// bit to the closure without invalidating prior analysis.
7002+
// If we have a ClosureExpr, then we can safely propagate @Sendable
7003+
// to the closure without invalidating prior analysis.
69817004
auto fromEI = fromFunc->getExtInfo();
69827005
if (toEI.isSendable() && !fromEI.isSendable()) {
69837006
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withConcurrent());
@@ -6991,6 +7014,22 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69917014
}
69927015
}
69937016

7017+
// If we have a ClosureExpr, then we can safely propagate the 'async'
7018+
// bit to the closure without invalidating prior analysis.
7019+
fromEI = fromFunc->getExtInfo();
7020+
if (toEI.isAsync() && !fromEI.isAsync() &&
7021+
shouldPropagateAsyncToClosure(expr)) {
7022+
auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync());
7023+
if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
7024+
fromFunc = newFromFuncType->castTo<FunctionType>();
7025+
7026+
// Propagating the 'concurrent' bit might have satisfied the entire
7027+
// conversion. If so, we're done, otherwise keep converting.
7028+
if (fromFunc->isEqual(toType))
7029+
return expr;
7030+
}
7031+
}
7032+
69947033
// If we have a ClosureExpr, then we can safely propagate a global actor
69957034
// to the closure without invalidating prior analysis.
69967035
fromEI = fromFunc->getExtInfo();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
138138
IGNORED_ATTR(UnsafeSendable)
139139
IGNORED_ATTR(UnsafeMainActor)
140140
IGNORED_ATTR(ImplicitSelfCapture)
141+
IGNORED_ATTR(InheritActorContext)
141142
#undef IGNORED_ATTR
142143

143144
void visitAlignmentAttr(AlignmentAttr *attr) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -773,19 +773,26 @@ static bool isAsyncCall(const ApplyExpr *call) {
773773
/// features.
774774
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc);
775775

776-
/// Determine whether this closure is escaping.
777-
static bool isSendableClosure(const AbstractClosureExpr *closure) {
776+
/// Determine whether this closure should be treated as Sendable.
777+
///
778+
/// \param forActorIsolation Whether this check is for the purposes of
779+
/// determining whether the closure must be non-isolated.
780+
static bool isSendableClosure(
781+
const AbstractClosureExpr *closure, bool forActorIsolation) {
782+
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
783+
if (forActorIsolation && explicitClosure->inheritsActorContext())
784+
return false;
785+
786+
if (explicitClosure->isUnsafeSendable())
787+
return true;
788+
}
789+
778790
if (auto type = closure->getType()) {
779791
if (auto fnType = type->getAs<AnyFunctionType>())
780792
if (fnType->isSendable())
781793
return true;
782794
}
783795

784-
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
785-
if (explicitClosure->isUnsafeSendable())
786-
return true;
787-
}
788-
789796
return false;
790797
}
791798

@@ -2113,7 +2120,7 @@ namespace {
21132120
}
21142121

21152122
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
2116-
if (isSendableClosure(closure)) {
2123+
if (isSendableClosure(closure, /*forActorIsolation=*/true)) {
21172124
return diag::actor_isolated_from_concurrent_closure;
21182125
}
21192126

@@ -2310,8 +2317,9 @@ namespace {
23102317
}
23112318
}
23122319

2313-
// Sendable closures are always actor-independent.
2314-
if (isSendableClosure(closure))
2320+
// Sendable closures are actor-independent unless the closure has
2321+
// specifically opted into inheriting actor isolation.
2322+
if (isSendableClosure(closure, /*forActorIsolation=*/true))
23152323
return ClosureActorIsolation::forIndependent();
23162324

23172325
// A non-escaping closure gets its isolation from its context.
@@ -2362,7 +2370,7 @@ bool ActorIsolationChecker::mayExecuteConcurrentlyWith(
23622370
while (useContext != defContext) {
23632371
// If we find a concurrent closure... it can be run concurrently.
23642372
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
2365-
if (isSendableClosure(closure))
2373+
if (isSendableClosure(closure, /*forActorIsolation=*/false))
23662374
return true;
23672375
}
23682376

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ namespace {
15471547
UNINTERESTING_ATTR(UnsafeSendable)
15481548
UNINTERESTING_ATTR(UnsafeMainActor)
15491549
UNINTERESTING_ATTR(ImplicitSelfCapture)
1550+
UNINTERESTING_ATTR(InheritActorContext)
15501551
#undef UNINTERESTING_ATTR
15511552

15521553
void visitAvailableAttr(AvailableAttr *attr) {

test/Concurrency/actor_isolation.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ actor MyActor: MySuperActor {
7070
class func synchronousClass() { }
7171
static func synchronousStatic() { }
7272

73-
func synchronous() -> String { text.first ?? "nothing" } // expected-note 19{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
73+
func synchronous() -> String { text.first ?? "nothing" } // expected-note 20{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
7474
func asynchronous() async -> String {
7575
super.superState += 4
7676
return synchronous()
@@ -754,3 +754,30 @@ func testCrossActorProtocol<T: P>(t: T) async {
754754
t.f() // expected-error{{call is 'async' but is not marked with 'await'}}
755755
t.g() // expected-error{{call is 'async' but is not marked with 'await'}}
756756
}
757+
758+
// ----------------------------------------------------------------------
759+
// @_inheritActorContext
760+
// ----------------------------------------------------------------------
761+
func acceptAsyncSendableClosure<T>(_: @Sendable () async -> T) { }
762+
func acceptAsyncSendableClosureInheriting<T>(@_inheritActorContext _: @Sendable () async -> T) { }
763+
764+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
765+
extension MyActor {
766+
func testSendableAndInheriting() {
767+
acceptAsyncSendableClosure {
768+
synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent closure}}
769+
}
770+
771+
acceptAsyncSendableClosure {
772+
await synchronous() // ok
773+
}
774+
775+
acceptAsyncSendableClosureInheriting {
776+
synchronous() // okay
777+
}
778+
779+
acceptAsyncSendableClosureInheriting {
780+
await synchronous() // expected-warning{{no 'async' operations occur within 'await' expression}}
781+
}
782+
}
783+
}

0 commit comments

Comments
 (0)