Skip to content

Commit 04d4676

Browse files
committed
[AST] Extend @_inheritActorContext attribute to support optional always modifier
By default (currently) the closure passed to a parameter with `@_inheritActorContext` would only inherit isolation from `nonisolated`, global actor isolated or actor context when "self" is captured by the closure. `always` changes this behavior to always inherit actor isolation from context regardless of whether it's captured or not.
1 parent 36de3a8 commit 04d4676

17 files changed

+249
-10
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,43 @@ inherit the actor context (i.e. what actor it should be run on) based on the
600600
declaration site of the closure rather than be non-Sendable. This does not do
601601
anything if the closure is synchronous.
602602

603+
This works with global actors as expected:
604+
605+
```swift
606+
@MainActor
607+
func test() {
608+
Task { /* main actor isolated */ }
609+
}
610+
```
611+
612+
However, for the inference to work with instance actors (i.e. `isolated` parameters),
613+
the closure must capture the isolated parameter explicitly:
614+
615+
```swift
616+
func test(actor: isolated (any Actor)) {
617+
Task { /* non isolated */ } // !!!
618+
}
619+
620+
func test(actor: isolated (any Actor)) {
621+
Task { // @_inheritActorContext
622+
_ = actor // 'actor'-isolated
623+
}
624+
}
625+
```
626+
627+
The attribute takes an optional modifier '`always`', which changes this behavior
628+
and *always* captures the enclosing isolated context, rather than forcing developers
629+
to perform the explicit capture themselfes:
630+
631+
```swift
632+
func test(actor: isolated (any Actor)) {
633+
Task.immediate { // @_inheritActorContext(always)
634+
// 'actor'-isolated!
635+
// (without having to capture 'actor explicitly')
636+
}
637+
}
638+
```
639+
603640
DISCUSSION: The reason why this does nothing when the closure is synchronous is
604641
since it does not have the ability to hop to the appropriate executor before it
605642
is run, so we may create concurrency errors.

include/swift/AST/Attr.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class DeclAttribute : public AttributeBase {
230230
Modifier : NumNonIsolatedModifierBits
231231
);
232232

233+
SWIFT_INLINE_BITFIELD(InheritActorContextAttr, DeclAttribute, NumInheritActorContextKindBits,
234+
Modifier : NumInheritActorContextKindBits
235+
);
236+
233237
SWIFT_INLINE_BITFIELD_FULL(AllowFeatureSuppressionAttr, DeclAttribute, 1+31,
234238
: NumPadBits,
235239
Inverted : 1,
@@ -3011,6 +3015,50 @@ class NonisolatedAttr final : public DeclAttribute {
30113015
}
30123016
};
30133017

3018+
/// Represents @_inheritActorContext modifier.
3019+
class InheritActorContextAttr final : public DeclAttribute {
3020+
public:
3021+
InheritActorContextAttr(SourceLoc atLoc, SourceRange range,
3022+
InheritActorContextModifier modifier, bool implicit)
3023+
: DeclAttribute(DeclAttrKind::InheritActorContext, atLoc, range,
3024+
implicit) {
3025+
Bits.InheritActorContextAttr.Modifier = static_cast<unsigned>(modifier);
3026+
assert((getModifier() == modifier) && "not enough bits for modifier");
3027+
}
3028+
3029+
InheritActorContextModifier getModifier() const {
3030+
return static_cast<InheritActorContextModifier>(
3031+
Bits.InheritActorContextAttr.Modifier);
3032+
}
3033+
3034+
bool isAlways() const {
3035+
return getModifier() == InheritActorContextModifier::Always;
3036+
}
3037+
3038+
static InheritActorContextAttr *
3039+
createImplicit(ASTContext &ctx, InheritActorContextModifier modifier =
3040+
InheritActorContextModifier::None) {
3041+
return new (ctx)
3042+
InheritActorContextAttr(/*atLoc*/ {}, /*range*/ {}, modifier,
3043+
/*implicit=*/true);
3044+
}
3045+
3046+
static bool classof(const DeclAttribute *DA) {
3047+
return DA->getKind() == DeclAttrKind::InheritActorContext;
3048+
}
3049+
3050+
/// Create a copy of this attribute.
3051+
InheritActorContextAttr *clone(ASTContext &ctx) const {
3052+
return new (ctx)
3053+
InheritActorContextAttr(AtLoc, Range, getModifier(), isImplicit());
3054+
}
3055+
3056+
bool isEquivalent(const InheritActorContextAttr *other,
3057+
Decl *attachedTo) const {
3058+
return getModifier() == other->getModifier();
3059+
}
3060+
};
3061+
30143062
/// A macro role attribute, spelled with either @attached or @freestanding,
30153063
/// which declares one of the roles that a given macro can inhabit.
30163064
class MacroRoleAttr final

include/swift/AST/AttrKind.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,22 @@ enum : unsigned {
142142
static_cast<unsigned>(NonIsolatedModifier::Last_NonIsolatedModifier))
143143
};
144144

145+
enum class InheritActorContextModifier : uint8_t {
146+
/// Inherit the actor execution context if the isolated parameter was
147+
/// captured by the closure, context is nonisolated or isolated to a
148+
/// global actor.
149+
None = 0,
150+
/// Always inherit the actor context, even when the isolated parameter
151+
/// for the context is not closed over explicitly.
152+
Always,
153+
Last_InheritActorContextKind = Always
154+
};
155+
156+
enum : unsigned {
157+
NumInheritActorContextKindBits = countBitsUsed(static_cast<unsigned>(
158+
InheritActorContextModifier::Last_InheritActorContextKind))
159+
};
160+
145161
enum class DeclAttrKind : unsigned {
146162
#define DECL_ATTR(_, CLASS, ...) CLASS,
147163
#define LAST_DECL_ATTR(CLASS) Last_DeclAttr = CLASS,

include/swift/AST/DeclAttr.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,10 @@ SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture,
620620
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr,
621621
115)
622622

623-
SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
623+
DECL_ATTR(_inheritActorContext, InheritActorContext,
624624
OnParam,
625-
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr,
625+
// since the _inheritActorContext(always) forces an actor capture, it changes ABI of the closure this applies to
626+
UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr,
626627
116)
627628

628629
SIMPLE_DECL_ATTR(_eagerMove, EagerMove,

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8595,6 +8595,13 @@ GROUPED_ERROR(isolated_conformance_wrong_domain,IsolatedConformances,none,
85958595
"%0 conformance of %1 to %2 cannot be used in %3 context",
85968596
(ActorIsolation, Type, DeclName, ActorIsolation))
85978597
8598+
//===----------------------------------------------------------------------===//
8599+
// MARK: @_inheritActorContext
8600+
//===----------------------------------------------------------------------===//
8601+
ERROR(inherit_actor_context_only_on_func_types,none,
8602+
"%0 only applies to parameters with function types (got: %1)",
8603+
(DeclAttribute, Type))
8604+
85988605
//===----------------------------------------------------------------------===//
85998606
// MARK: @concurrent and nonisolated(nonsending) attributes
86008607
//===----------------------------------------------------------------------===//

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ IDENTIFIER(SerializationRequirement)
325325
IDENTIFIER_WITH_NAME(builderSelf, "$builderSelf")
326326

327327
// Attribute options
328+
IDENTIFIER(always)
328329
IDENTIFIER_(_always)
329330
IDENTIFIER_(assumed)
330331
IDENTIFIER(checked)

include/swift/Parse/IDEInspectionCallbacks.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ enum class ParameterizedDeclAttributeKind {
3939
Available,
4040
FreestandingMacro,
4141
AttachedMacro,
42-
StorageRestrictions
42+
StorageRestrictions,
43+
InheritActorContext
4344
};
4445

4546
/// A bit of a hack. When completing inside the '@storageRestrictions'

lib/AST/ASTDumper.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4976,7 +4976,6 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
49764976
TRIVIAL_ATTR_PRINTER(ImplicitSelfCapture, implicit_self_capture)
49774977
TRIVIAL_ATTR_PRINTER(Indirect, indirect)
49784978
TRIVIAL_ATTR_PRINTER(Infix, infix)
4979-
TRIVIAL_ATTR_PRINTER(InheritActorContext, inherit_actor_context)
49804979
TRIVIAL_ATTR_PRINTER(InheritsConvenienceInitializers,
49814980
inherits_convenience_initializers)
49824981
TRIVIAL_ATTR_PRINTER(Inlinable, inlinable)
@@ -5301,6 +5300,12 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
53015300
printFlag(Attr->isNonSending(), "nonsending");
53025301
printFoot();
53035302
}
5303+
void visitInheritActorContextAttr(InheritActorContextAttr *Attr,
5304+
Label label) {
5305+
printCommon(Attr, "inherit_actor_context_attr", label);
5306+
printFlag(Attr->isAlways(), "always");
5307+
printFoot();
5308+
}
53045309
void visitObjCAttr(ObjCAttr *Attr, Label label) {
53055310
printCommon(Attr, "objc_attr", label);
53065311
if (Attr->hasName())

lib/AST/Attr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,18 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
15311531
break;
15321532
}
15331533

1534+
case DeclAttrKind::InheritActorContext: {
1535+
Printer.printAttrName("@_inheritActorContext");
1536+
switch (cast<InheritActorContextAttr>(this)->getModifier()) {
1537+
case InheritActorContextModifier::None:
1538+
break;
1539+
case InheritActorContextModifier::Always:
1540+
Printer << "(always)";
1541+
break;
1542+
}
1543+
break;
1544+
}
1545+
15341546
case DeclAttrKind::MacroRole: {
15351547
auto Attr = cast<MacroRoleAttr>(this);
15361548

@@ -1915,6 +1927,13 @@ StringRef DeclAttribute::getAttrName() const {
19151927
case NonIsolatedModifier::NonSending:
19161928
return "nonisolated(nonsending)";
19171929
}
1930+
case DeclAttrKind::InheritActorContext:
1931+
switch (cast<InheritActorContextAttr>(this)->getModifier()) {
1932+
case InheritActorContextModifier::None:
1933+
return "_inheritActorContext";
1934+
case InheritActorContextModifier::Always:
1935+
return "_inheritActorContext(always)";
1936+
}
19181937
case DeclAttrKind::MacroRole:
19191938
switch (cast<MacroRoleAttr>(this)->getMacroSyntax()) {
19201939
case MacroSyntax::Freestanding:

lib/IDE/CompletionLookup.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,6 +3141,9 @@ void CompletionLookup::getAttributeDeclParamCompletions(
31413141
addDeclAttrParamKeyword("unsafe", /*Parameters=*/{}, "", false);
31423142
addDeclAttrParamKeyword("nonsending", /*Parameters=*/{}, "", false);
31433143
break;
3144+
case ParameterizedDeclAttributeKind::InheritActorContext:
3145+
addDeclAttrParamKeyword("always", /*Parameters=*/{}, "", false);
3146+
break;
31443147
case ParameterizedDeclAttributeKind::AccessControl:
31453148
addDeclAttrParamKeyword("set", /*Parameters=*/{}, "", false);
31463149
break;

0 commit comments

Comments
 (0)