@@ -697,6 +697,12 @@ namespace {
697
697
698
698
ConcurrentExecutionChecker concurrentExecutionChecker;
699
699
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
+
700
706
const DeclContext *getDeclContext () const {
701
707
return contextStack.back ();
702
708
}
@@ -709,6 +715,33 @@ namespace {
709
715
useContext, defContext);
710
716
}
711
717
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
+
712
745
public:
713
746
ActorIsolationChecker (const DeclContext *dc) : ctx(dc->getASTContext ()) {
714
747
contextStack.push_back (dc);
@@ -777,6 +810,12 @@ namespace {
777
810
if (auto inout = dyn_cast<InOutExpr>(expr)) {
778
811
if (!applyStack.empty ())
779
812
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 ());
780
819
}
781
820
782
821
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
@@ -786,7 +825,8 @@ namespace {
786
825
}
787
826
788
827
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
789
- checkNonMemberReference (declRef->getDeclRef (), declRef->getLoc ());
828
+ checkNonMemberReference (
829
+ declRef->getDeclRef (), declRef->getLoc (), declRef);
790
830
return { true , expr };
791
831
}
792
832
@@ -865,6 +905,11 @@ namespace {
865
905
applyStack.pop_back ();
866
906
}
867
907
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
+
868
913
return expr;
869
914
}
870
915
@@ -1216,7 +1261,8 @@ namespace {
1216
1261
}
1217
1262
1218
1263
// / 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) {
1220
1266
if (!valueRef)
1221
1267
return false ;
1222
1268
@@ -1234,22 +1280,37 @@ namespace {
1234
1280
value, loc, isolation.getGlobalActor ());
1235
1281
1236
1282
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 ()))
1238
1287
return false ;
1239
1288
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.
1244
1301
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 ());
1249
1304
return true ;
1250
1305
}
1251
1306
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 ;
1253
1314
1254
1315
case ActorIsolationRestriction::Unsafe:
1255
1316
return diagnoseReferenceToUnsafeGlobal (value, loc);
@@ -1536,6 +1597,10 @@ SourceLoc ConcurrentExecutionChecker::getConcurrentReferenceLoc(
1536
1597
enclosingBody = enclosingFunc->getBody ();
1537
1598
else if (auto enclosingClosure = dyn_cast<ClosureExpr>(enclosingDC))
1538
1599
enclosingBody = enclosingClosure->getBody ();
1600
+ else if (auto enclosingTopLevelCode = dyn_cast<TopLevelCodeDecl>(enclosingDC))
1601
+ enclosingBody = enclosingTopLevelCode->getBody ();
1602
+ else
1603
+ return SourceLoc ();
1539
1604
1540
1605
assert (enclosingBody && " Cannot have a local function here" );
1541
1606
ConcurrentLocalRefWalker walker (*this , localFunc);
@@ -1549,10 +1614,8 @@ bool ConcurrentExecutionChecker::mayExecuteConcurrentlyWith(
1549
1614
// Walk the context chain from the use to the definition.
1550
1615
while (useContext != defContext) {
1551
1616
// 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.
1554
1617
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
1555
- if (isEscapingClosure (closure) || isConcurrentClosure (closure))
1618
+ if (isConcurrentClosure (closure))
1556
1619
return true ;
1557
1620
}
1558
1621
@@ -1963,18 +2026,21 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
1963
2026
static bool shouldDiagnoseExistingDataRaces (const DeclContext *dc) {
1964
2027
while (!dc->isModuleScopeContext ()) {
1965
2028
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
+ }
1969
2035
} else if (auto decl = dc->getAsDecl ()) {
1970
2036
// If any isolation attributes are present, we're using concurrency
1971
2037
// features.
1972
2038
if (getIsolationFromAttributes (decl, /* shouldDiagnose=*/ false ))
1973
2039
return true ;
1974
2040
1975
2041
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 () )
1978
2044
return true ;
1979
2045
1980
2046
// If there is an explicit @asyncHandler, we're using concurrency
0 commit comments