Skip to content

Commit 917524d

Browse files
authored
Merge pull request #81496 from xedin/inheritActorContext-alwayse
[AST/Sema/SIL] Implement `@_inheritActorContext(always)`
2 parents ccaf7e1 + 17b8f7e commit 917524d

33 files changed

+583
-288
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/ASTBridging.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,18 @@ BridgedNonisolatedAttr_createParsed(BridgedASTContext cContext,
12591259
BridgedSourceRange cRange,
12601260
BridgedNonIsolatedModifier modifier);
12611261

1262+
enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedInheritActorContextModifier {
1263+
BridgedInheritActorContextModifierNone,
1264+
BridgedInheritActorContextModifierAlways,
1265+
};
1266+
1267+
SWIFT_NAME("BridgedInheritActorContextAttr.createParsed(_:atLoc:range:modifier:)")
1268+
BridgedInheritActorContextAttr
1269+
BridgedInheritActorContextAttr_createParsed(BridgedASTContext cContext,
1270+
BridgedSourceLoc cAtLoc,
1271+
BridgedSourceRange cRange,
1272+
BridgedInheritActorContextModifier modifier);
1273+
12621274
SWIFT_NAME("BridgedObjCAttr.createParsedUnnamed(_:atLoc:attrNameLoc:)")
12631275
BridgedObjCAttr
12641276
BridgedObjCAttr_createParsedUnnamed(BridgedASTContext cContext,

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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8595,6 +8595,23 @@ 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+
8605+
ERROR(inherit_actor_context_only_on_sending_or_Sendable_params,none,
8606+
"%0 only applies to 'sending' parameters or parameters with "
8607+
"'@Sendable' function types",
8608+
(DeclAttribute))
8609+
8610+
ERROR(inherit_actor_context_only_on_async_or_isolation_erased_params,none,
8611+
"%0 only applies to '@isolated(any)' parameters or parameters with "
8612+
"asynchronous function types",
8613+
(DeclAttribute))
8614+
85988615
//===----------------------------------------------------------------------===//
85998616
// MARK: @concurrent and nonisolated(nonsending) attributes
86008617
//===----------------------------------------------------------------------===//

include/swift/AST/Expr.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
267267
Kind : 2
268268
);
269269

270-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1,
270+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1,
271271
/// True if closure parameters were synthesized from anonymous closure
272272
/// variables.
273273
HasAnonymousClosureVars : 1,
@@ -276,9 +276,11 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
276276
/// on each member reference.
277277
ImplicitSelfCapture : 1,
278278

279-
/// True if this @Sendable async closure parameter should implicitly
280-
/// inherit the actor context from where it was formed.
279+
/// True if this closure parameter should implicitly inherit the actor
280+
/// context from where it was formed.
281281
InheritActorContext : 1,
282+
/// The kind for inheritance - none or always at the moment.
283+
InheritActorContextKind : 1,
282284

283285
/// True if this closure's actor isolation behavior was determined by an
284286
/// \c \@preconcurrency declaration.
@@ -4318,6 +4320,7 @@ class ClosureExpr : public AbstractClosureExpr {
43184320
Bits.ClosureExpr.HasAnonymousClosureVars = false;
43194321
Bits.ClosureExpr.ImplicitSelfCapture = false;
43204322
Bits.ClosureExpr.InheritActorContext = false;
4323+
Bits.ClosureExpr.InheritActorContextKind = 0;
43214324
Bits.ClosureExpr.IsPassedToSendingParameter = false;
43224325
Bits.ClosureExpr.NoGlobalActorAttribute = false;
43234326
Bits.ClosureExpr.RequiresDynamicIsolationChecking = false;
@@ -4366,8 +4369,29 @@ class ClosureExpr : public AbstractClosureExpr {
43664369
return Bits.ClosureExpr.InheritActorContext;
43674370
}
43684371

4369-
void setInheritsActorContext(bool value = true) {
4372+
/// Whether this closure should _always_ implicitly inherit the actor context
4373+
/// regardless of whether the isolation parameter is captured or not.
4374+
bool alwaysInheritsActorContext() const {
4375+
if (!inheritsActorContext())
4376+
return false;
4377+
return getInheritActorIsolationModifier() ==
4378+
InheritActorContextModifier::Always;
4379+
}
4380+
4381+
void setInheritsActorContext(bool value = true,
4382+
InheritActorContextModifier modifier =
4383+
InheritActorContextModifier::None) {
43704384
Bits.ClosureExpr.InheritActorContext = value;
4385+
Bits.ClosureExpr.InheritActorContextKind = uint8_t(modifier);
4386+
assert((static_cast<InheritActorContextModifier>(
4387+
Bits.ClosureExpr.InheritActorContextKind) == modifier) &&
4388+
"not enough bits for modifier");
4389+
}
4390+
4391+
InheritActorContextModifier getInheritActorIsolationModifier() const {
4392+
assert(inheritsActorContext());
4393+
return static_cast<InheritActorContextModifier>(
4394+
Bits.ClosureExpr.InheritActorContextKind);
43714395
}
43724396

43734397
/// Whether the closure's concurrency behavior was determined by an

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/AST/Types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,7 @@ struct ParameterListInfo {
40494049
SmallBitVector propertyWrappers;
40504050
SmallBitVector implicitSelfCapture;
40514051
SmallBitVector inheritActorContext;
4052+
SmallBitVector alwaysInheritActorContext;
40524053
SmallBitVector variadicGenerics;
40534054
SmallBitVector sendingParameters;
40544055

@@ -4075,7 +4076,8 @@ struct ParameterListInfo {
40754076

40764077
/// Whether the given parameter is a closure that should inherit the
40774078
/// actor context from the context in which it was created.
4078-
bool inheritsActorContext(unsigned paramIdx) const;
4079+
std::pair<bool, InheritActorContextModifier>
4080+
inheritsActorContext(unsigned paramIdx) const;
40794081

40804082
bool isVariadicGenericParameter(unsigned paramIdx) const;
40814083

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ LANGUAGE_FEATURE(IsolatedConformances, 407, "Global-actor isolated conformances"
259259
LANGUAGE_FEATURE(ValueGenericsNameLookup, 452, "Value generics appearing as static members for namelookup")
260260
LANGUAGE_FEATURE(GeneralizedIsSameMetaTypeBuiltin, 465, "Builtin.is_same_metatype with support for noncopyable/nonescapable types")
261261
SUPPRESSIBLE_LANGUAGE_FEATURE(ABIAttributeSE0479, 479, "@abi attribute on functions, initializers, properties, and subscripts")
262+
LANGUAGE_FEATURE(AlwaysInheritActorContext, 472, "@_inheritActorContext(always)")
262263

263264
// Swift 6
264265
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)

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/AST/Bridging/DeclAttributeBridging.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,25 @@ BridgedNonisolatedAttr_createParsed(BridgedASTContext cContext,
649649
/*implicit=*/false);
650650
}
651651

652+
static InheritActorContextModifier
653+
unbridged(BridgedInheritActorContextModifier modifier) {
654+
switch (modifier) {
655+
case BridgedInheritActorContextModifierNone:
656+
return InheritActorContextModifier::None;
657+
case BridgedInheritActorContextModifierAlways:
658+
return InheritActorContextModifier::Always;
659+
}
660+
llvm_unreachable("unhandled enum value");
661+
}
662+
663+
BridgedInheritActorContextAttr BridgedInheritActorContextAttr_createParsed(
664+
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
665+
BridgedSourceRange cRange, BridgedInheritActorContextModifier modifier) {
666+
return new (cContext.unbridged()) InheritActorContextAttr(
667+
cAtLoc.unbridged(), cRange.unbridged(), unbridged(modifier),
668+
/*implicit=*/false);
669+
}
670+
652671
BridgedObjCAttr
653672
BridgedObjCAttr_createParsedUnnamed(BridgedASTContext cContext,
654673
BridgedSourceLoc cAtLoc,

lib/AST/FeatureSet.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,21 @@ static bool usesFeatureExtensibleAttribute(Decl *decl) {
624624
return decl->getAttrs().hasAttribute<ExtensibleAttr>();
625625
}
626626

627+
static bool usesFeatureAlwaysInheritActorContext(Decl *decl) {
628+
auto *VD = dyn_cast<ValueDecl>(decl);
629+
if (!VD)
630+
return false;
631+
632+
if (auto *PL = VD->getParameterList()) {
633+
return llvm::any_of(*PL, [&](const ParamDecl *P) {
634+
auto *attr = P->getAttrs().getAttribute<InheritActorContextAttr>();
635+
return attr && attr->isAlways();
636+
});
637+
}
638+
639+
return false;
640+
}
641+
627642
// ----------------------------------------------------------------------------
628643
// MARK: - FeatureSet
629644
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)