[Sema] Start propagating @_inheritActorContext(always) attribute to closures

This commit is contained in:
Pavel Yaskevich
2025-05-13 11:05:22 -07:00
parent c0aca5384b
commit a4f6d710cf
6 changed files with 106 additions and 35 deletions

View File

@@ -267,7 +267,7 @@ protected:
Kind : 2
);
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1,
/// True if closure parameters were synthesized from anonymous closure
/// variables.
HasAnonymousClosureVars : 1,
@@ -276,9 +276,11 @@ protected:
/// on each member reference.
ImplicitSelfCapture : 1,
/// True if this @Sendable async closure parameter should implicitly
/// inherit the actor context from where it was formed.
/// True if this closure parameter should implicitly inherit the actor
/// context from where it was formed.
InheritActorContext : 1,
/// The kind for inheritance - none or always at the moment.
InheritActorContextKind : 1,
/// True if this closure's actor isolation behavior was determined by an
/// \c \@preconcurrency declaration.
@@ -4318,6 +4320,7 @@ public:
Bits.ClosureExpr.HasAnonymousClosureVars = false;
Bits.ClosureExpr.ImplicitSelfCapture = false;
Bits.ClosureExpr.InheritActorContext = false;
Bits.ClosureExpr.InheritActorContextKind = 0;
Bits.ClosureExpr.IsPassedToSendingParameter = false;
Bits.ClosureExpr.NoGlobalActorAttribute = false;
Bits.ClosureExpr.RequiresDynamicIsolationChecking = false;
@@ -4366,8 +4369,29 @@ public:
return Bits.ClosureExpr.InheritActorContext;
}
void setInheritsActorContext(bool value = true) {
/// Whether this closure should _always_ implicitly inherit the actor context
/// regardless of whether the isolation parameter is captured or not.
bool alwaysInheritsActorContext() const {
if (!inheritsActorContext())
return false;
return getInheritActorIsolationModifier() ==
InheritActorContextModifier::Always;
}
void setInheritsActorContext(bool value = true,
InheritActorContextModifier modifier =
InheritActorContextModifier::None) {
Bits.ClosureExpr.InheritActorContext = value;
Bits.ClosureExpr.InheritActorContextKind = uint8_t(modifier);
assert((static_cast<InheritActorContextModifier>(
Bits.ClosureExpr.InheritActorContextKind) == modifier) &&
"not enough bits for modifier");
}
InheritActorContextModifier getInheritActorIsolationModifier() const {
assert(inheritsActorContext());
return static_cast<InheritActorContextModifier>(
Bits.ClosureExpr.InheritActorContextKind);
}
/// Whether the closure's concurrency behavior was determined by an

View File

@@ -4049,6 +4049,7 @@ struct ParameterListInfo {
SmallBitVector propertyWrappers;
SmallBitVector implicitSelfCapture;
SmallBitVector inheritActorContext;
SmallBitVector alwaysInheritActorContext;
SmallBitVector variadicGenerics;
SmallBitVector sendingParameters;
@@ -4075,7 +4076,8 @@ public:
/// Whether the given parameter is a closure that should inherit the
/// actor context from the context in which it was created.
bool inheritsActorContext(unsigned paramIdx) const;
std::pair<bool, InheritActorContextModifier>
inheritsActorContext(unsigned paramIdx) const;
bool isVariadicGenericParameter(unsigned paramIdx) const;

View File

@@ -1378,6 +1378,7 @@ ParameterListInfo::ParameterListInfo(
propertyWrappers.resize(params.size());
implicitSelfCapture.resize(params.size());
inheritActorContext.resize(params.size());
alwaysInheritActorContext.resize(params.size());
variadicGenerics.resize(params.size());
sendingParameters.resize(params.size());
@@ -1434,8 +1435,13 @@ ParameterListInfo::ParameterListInfo(
implicitSelfCapture.set(i);
}
if (param->getAttrs().hasAttribute<InheritActorContextAttr>()) {
inheritActorContext.set(i);
if (auto *attr =
param->getAttrs().getAttribute<InheritActorContextAttr>()) {
if (attr->isAlways()) {
alwaysInheritActorContext.set(i);
} else {
inheritActorContext.set(i);
}
}
if (param->getInterfaceType()->is<PackExpansionType>()) {
@@ -1469,10 +1475,18 @@ bool ParameterListInfo::isImplicitSelfCapture(unsigned paramIdx) const {
: false;
}
bool ParameterListInfo::inheritsActorContext(unsigned paramIdx) const {
return paramIdx < inheritActorContext.size()
? inheritActorContext[paramIdx]
: false;
std::pair<bool, InheritActorContextModifier>
ParameterListInfo::inheritsActorContext(unsigned paramIdx) const {
if (paramIdx >= inheritActorContext.size())
return std::make_pair(false, InheritActorContextModifier::None);
if (inheritActorContext[paramIdx])
return std::make_pair(true, InheritActorContextModifier::None);
if (alwaysInheritActorContext[paramIdx])
return std::make_pair(true, InheritActorContextModifier::Always);
return std::make_pair(false, InheritActorContextModifier::None);
}
bool ParameterListInfo::isVariadicGenericParameter(unsigned paramIdx) const {

View File

@@ -6097,15 +6097,20 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
}
/// Apply the contextually Sendable flag to the given expression,
static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture,
bool inheritActorContext,
bool isPassedToSendingParameter,
static void applyContextualClosureFlags(Expr *expr, unsigned paramIdx,
const ParameterListInfo &paramInfo,
bool requiresDynamicIsolationChecking,
bool isMacroArg) {
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
closure->setInheritsActorContext(inheritActorContext);
closure->setIsPassedToSendingParameter(isPassedToSendingParameter);
closure->setAllowsImplicitSelfCapture(
paramInfo.isImplicitSelfCapture(paramIdx));
auto [inheritActorContext, modifier] =
paramInfo.inheritsActorContext(paramIdx);
closure->setInheritsActorContext(inheritActorContext, modifier);
closure->setIsPassedToSendingParameter(
paramInfo.isSendingParameter(paramIdx));
closure->setRequiresDynamicIsolationChecking(
requiresDynamicIsolationChecking);
closure->setIsMacroArgument(isMacroArg);
@@ -6113,19 +6118,14 @@ static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture,
}
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
applyContextualClosureFlags(captureList->getClosureBody(),
implicitSelfCapture, inheritActorContext,
isPassedToSendingParameter,
requiresDynamicIsolationChecking,
applyContextualClosureFlags(captureList->getClosureBody(), paramIdx,
paramInfo, requiresDynamicIsolationChecking,
isMacroArg);
}
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
applyContextualClosureFlags(identity->getSubExpr(), implicitSelfCapture,
inheritActorContext,
isPassedToSendingParameter,
requiresDynamicIsolationChecking,
isMacroArg);
applyContextualClosureFlags(identity->getSubExpr(), paramIdx, paramInfo,
requiresDynamicIsolationChecking, isMacroArg);
}
}
@@ -6251,19 +6251,13 @@ ArgumentList *ExprRewriter::coerceCallArguments(
auto applyFlagsToArgument = [&paramInfo,
&closuresRequireDynamicIsolationChecking,
&locator](
unsigned paramIdx, Expr *argument) {
&locator](unsigned paramIdx, Expr *argument) {
if (!isClosureLiteralExpr(argument))
return;
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
bool isPassedToSendingParameter = paramInfo.isSendingParameter(paramIdx);
bool isMacroArg = isExpr<MacroExpansionExpr>(locator.getAnchor());
applyContextualClosureFlags(argument, isImplicitSelfCapture,
inheritsActorContext,
isPassedToSendingParameter,
applyContextualClosureFlags(argument, paramIdx, paramInfo,
closuresRequireDynamicIsolationChecking,
isMacroArg);
};

View File

@@ -4774,6 +4774,11 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation(
case ActorIsolation::ActorInstance: {
if (checkIsolatedCapture) {
auto *explicitClosure = dyn_cast<ClosureExpr>(closure);
// @_inheritActorContext(always) forces the isolation capture.
if (explicitClosure && explicitClosure->alwaysInheritsActorContext())
return parentIsolation;
if (auto param = closure->getCaptureInfo().getIsolatedParamCapture())
return ActorIsolation::forActorInstanceCapture(param);
} else {

View File

@@ -1210,6 +1210,8 @@ actor MyServer : Server {
func acceptAsyncSendableClosure<T>(_: @Sendable () async -> T) { }
@available(SwiftStdlib 5.1, *)
func acceptAsyncSendableClosureInheriting<T>(@_inheritActorContext _: @Sendable () async -> T) { }
@available(SwiftStdlib 5.1, *)
func acceptAsyncSendableClosureInheritingAlways<T>(@_inheritActorContext(always) _: @Sendable () async -> T) { }
@available(SwiftStdlib 5.1, *)
extension MyActor {
@@ -1237,6 +1239,10 @@ extension MyActor {
_ = await synchronous() // expected-warning{{no 'async' operations occur within 'await' expression}}
counter += 1 // okay
}
acceptAsyncSendableClosureInheritingAlways {
counter += 1 // Ok
}
}
}
@@ -1257,6 +1263,32 @@ func testGlobalActorInheritance() {
acceptAsyncSendableClosureInheriting {
counter += 1 // ok
}
acceptAsyncSendableClosureInheritingAlways {
counter += 1 // ok
}
}
@available(SwiftStdlib 5.1, *)
func testIsolatedParameter1(_: isolated any Actor, v: inout Int) {
acceptAsyncSendableClosureInheriting {
v += 1 // expected-warning {{mutable capture of 'inout' parameter 'v' is not allowed in concurrently-executing code}}
}
acceptAsyncSendableClosureInheritingAlways {
v += 1 // Ok
}
}
@available(SwiftStdlib 5.1, *)
func testIsolatedParameter2(_: isolated (any Actor)? = #isolation, v: inout Int) {
acceptAsyncSendableClosureInheriting {
v += 1 // expected-warning {{mutable capture of 'inout' parameter 'v' is not allowed in concurrently-executing code}}
}
acceptAsyncSendableClosureInheritingAlways {
v += 1 // Ok
}
}
@available(SwiftStdlib 5.1, *)
@@ -1763,4 +1795,4 @@ actor UserDefinedActorSelfDotMethod {
// error message changes with InferSendabaleFromCaptures - see actor_isolation_swift6.swift
return functionRef // expected-error {{cannot convert return expression of type '(isolated Self) -> () -> ()' to return type '(UserDefinedActorSelfDotMethod) -> @isolated(any) () -> Void'}}
}
}
}