@@ -697,6 +697,12 @@ namespace {
697697
698698 ConcurrentExecutionChecker concurrentExecutionChecker;
699699
700+ using MutableVarParent = llvm::PointerUnion<InOutExpr *, LoadExpr *>;
701+
702+ // / Mapping from mutable local variables to the parent expression, when
703+ // / that parent is either a load or a inout expression.
704+ llvm::SmallDenseMap<DeclRefExpr *, MutableVarParent, 4 > mutableLocalVarParent;
705+
700706 const DeclContext *getDeclContext () const {
701707 return contextStack.back ();
702708 }
@@ -709,6 +715,33 @@ namespace {
709715 useContext, defContext);
710716 }
711717
718+ // / If the subexpression is a reference to a mutable local variable from a
719+ // / different context, record its parent. We'll query this as part of
720+ // / capture semantics in concurrent functions.
721+ void recordMutableVarParent (MutableVarParent parent, Expr *subExpr) {
722+ auto declRef = dyn_cast<DeclRefExpr>(subExpr);
723+ if (!declRef)
724+ return ;
725+
726+ auto var = dyn_cast_or_null<VarDecl>(declRef->getDecl ());
727+ if (!var)
728+ return ;
729+
730+ // Only mutable variables matter.
731+ if (!var->supportsMutation ())
732+ return ;
733+
734+ // Only mutable variables outside of the current context. This is an
735+ // optimization, because the parent map won't be queried in this case, and
736+ // it is the most common case for variables to be referenced in their
737+ // own context.
738+ if (var->getDeclContext () == getDeclContext ())
739+ return ;
740+
741+ assert (mutableLocalVarParent[declRef].isNull ());
742+ mutableLocalVarParent[declRef] = parent;
743+ }
744+
712745 public:
713746 ActorIsolationChecker (const DeclContext *dc) : ctx(dc->getASTContext ()) {
714747 contextStack.push_back (dc);
@@ -777,6 +810,12 @@ namespace {
777810 if (auto inout = dyn_cast<InOutExpr>(expr)) {
778811 if (!applyStack.empty ())
779812 diagnoseInOutArg (applyStack.back (), inout, false );
813+
814+ recordMutableVarParent (inout, inout->getSubExpr ());
815+ }
816+
817+ if (auto load = dyn_cast<LoadExpr>(expr)) {
818+ recordMutableVarParent (load, load->getSubExpr ());
780819 }
781820
782821 if (auto lookup = dyn_cast<LookupExpr>(expr)) {
@@ -786,7 +825,8 @@ namespace {
786825 }
787826
788827 if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
789- checkNonMemberReference (declRef->getDeclRef (), declRef->getLoc ());
828+ checkNonMemberReference (
829+ declRef->getDeclRef (), declRef->getLoc (), declRef);
790830 return { true , expr };
791831 }
792832
@@ -865,6 +905,11 @@ namespace {
865905 applyStack.pop_back ();
866906 }
867907
908+ // Clear out the mutable local variable parent map on the way out.
909+ if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr)) {
910+ mutableLocalVarParent.erase (declRefExpr);
911+ }
912+
868913 return expr;
869914 }
870915
@@ -1216,7 +1261,8 @@ namespace {
12161261 }
12171262
12181263 // / Check a reference to a local or global.
1219- bool checkNonMemberReference (ConcreteDeclRef valueRef, SourceLoc loc) {
1264+ bool checkNonMemberReference (
1265+ ConcreteDeclRef valueRef, SourceLoc loc, DeclRefExpr *declRefExpr) {
12201266 if (!valueRef)
12211267 return false ;
12221268
@@ -1234,22 +1280,37 @@ namespace {
12341280 value, loc, isolation.getGlobalActor ());
12351281
12361282 case ActorIsolationRestriction::LocalCapture:
1237- if (!shouldDiagnoseExistingDataRaces (getDeclContext ()))
1283+ // Check whether we are in a context that will not execute concurrently
1284+ // with the context of 'self'. If not, it's safe.
1285+ if (!mayExecuteConcurrentlyWith (
1286+ getDeclContext (), isolation.getLocalContext ()))
12381287 return false ;
12391288
1240- // Check whether we are in a context that will not execute concurrently
1241- // with the context of 'self'.
1242- if (mayExecuteConcurrentlyWith (
1243- getDeclContext (), isolation.getLocalContext ())) {
1289+ // Check whether this is a local variable, in which case we can
1290+ // determine whether it was captured by value.
1291+ if (auto var = dyn_cast<VarDecl>(value)) {
1292+ auto parent = mutableLocalVarParent[declRefExpr];
1293+
1294+ // If we have an immediate load of this variable, the by-value
1295+ // capture in a concurrent function will guarantee that this is
1296+ // safe.
1297+ if (parent.dyn_cast <LoadExpr *>())
1298+ return false ;
1299+
1300+ // Otherwise, we have concurrent mutation. Complain.
12441301 ctx.Diags .diagnose (
1245- loc, diag::concurrent_access_local,
1246- value->getDescriptiveKind (), value->getName ());
1247- value->diagnose (
1248- diag::kind_declared_here, value->getDescriptiveKind ());
1302+ loc, diag::concurrent_mutation_of_local_capture,
1303+ var->getDescriptiveKind (), var->getName ());
12491304 return true ;
12501305 }
12511306
1252- return false ;
1307+ // Concurrent access to some other local.
1308+ ctx.Diags .diagnose (
1309+ loc, diag::concurrent_access_local,
1310+ value->getDescriptiveKind (), value->getName ());
1311+ value->diagnose (
1312+ diag::kind_declared_here, value->getDescriptiveKind ());
1313+ return true ;
12531314
12541315 case ActorIsolationRestriction::Unsafe:
12551316 return diagnoseReferenceToUnsafeGlobal (value, loc);
@@ -1536,6 +1597,10 @@ SourceLoc ConcurrentExecutionChecker::getConcurrentReferenceLoc(
15361597 enclosingBody = enclosingFunc->getBody ();
15371598 else if (auto enclosingClosure = dyn_cast<ClosureExpr>(enclosingDC))
15381599 enclosingBody = enclosingClosure->getBody ();
1600+ else if (auto enclosingTopLevelCode = dyn_cast<TopLevelCodeDecl>(enclosingDC))
1601+ enclosingBody = enclosingTopLevelCode->getBody ();
1602+ else
1603+ return SourceLoc ();
15391604
15401605 assert (enclosingBody && " Cannot have a local function here" );
15411606 ConcurrentLocalRefWalker walker (*this , localFunc);
@@ -1549,10 +1614,8 @@ bool ConcurrentExecutionChecker::mayExecuteConcurrentlyWith(
15491614 // Walk the context chain from the use to the definition.
15501615 while (useContext != defContext) {
15511616 // If we find a concurrent closure... it can be run concurrently.
1552- // NOTE: We also classify escaping closures this way, which detects more
1553- // problematic cases.
15541617 if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
1555- if (isEscapingClosure (closure) || isConcurrentClosure (closure))
1618+ if (isConcurrentClosure (closure))
15561619 return true ;
15571620 }
15581621
@@ -1963,18 +2026,21 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
19632026static bool shouldDiagnoseExistingDataRaces (const DeclContext *dc) {
19642027 while (!dc->isModuleScopeContext ()) {
19652028 if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
1966- // Async closures use concurrency features.
1967- if (closure->getType () && closure->isBodyAsync ())
1968- return true ;
2029+ // Async and concurrent closures use concurrency features.
2030+ if (auto closureType = closure->getType ()) {
2031+ if (auto fnType = closureType->getAs <AnyFunctionType>())
2032+ if (fnType->isAsync () || fnType->isConcurrent ())
2033+ return true ;
2034+ }
19692035 } else if (auto decl = dc->getAsDecl ()) {
19702036 // If any isolation attributes are present, we're using concurrency
19712037 // features.
19722038 if (getIsolationFromAttributes (decl, /* shouldDiagnose=*/ false ))
19732039 return true ;
19742040
19752041 if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
1976- // Async functions use concurrency features.
1977- if (func->hasAsync ())
2042+ // Async and concurrent functions use concurrency features.
2043+ if (func->hasAsync () || func-> isConcurrent () )
19782044 return true ;
19792045
19802046 // If there is an explicit @asyncHandler, we're using concurrency
0 commit comments