@@ -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
@@ -1215,7 +1260,8 @@ namespace {
1215
1260
}
1216
1261
1217
1262
// / Check a reference to a local or global.
1218
- bool checkNonMemberReference (ConcreteDeclRef valueRef, SourceLoc loc) {
1263
+ bool checkNonMemberReference (
1264
+ ConcreteDeclRef valueRef, SourceLoc loc, DeclRefExpr *declRefExpr) {
1219
1265
if (!valueRef)
1220
1266
return false ;
1221
1267
@@ -1233,22 +1279,37 @@ namespace {
1233
1279
value, loc, isolation.getGlobalActor ());
1234
1280
1235
1281
case ActorIsolationRestriction::LocalCapture:
1236
- if (!shouldDiagnoseExistingDataRaces (getDeclContext ()))
1282
+ // Check whether we are in a context that will not execute concurrently
1283
+ // with the context of 'self'. If not, it's safe.
1284
+ if (!mayExecuteConcurrentlyWith (
1285
+ getDeclContext (), isolation.getLocalContext ()))
1237
1286
return false ;
1238
1287
1239
- // Check whether we are in a context that will not execute concurrently
1240
- // with the context of 'self'.
1241
- if (mayExecuteConcurrentlyWith (
1242
- getDeclContext (), isolation.getLocalContext ())) {
1288
+ // Check whether this is a local variable, in which case we can
1289
+ // determine whether it was captured by value.
1290
+ if (auto var = dyn_cast<VarDecl>(value)) {
1291
+ auto parent = mutableLocalVarParent[declRefExpr];
1292
+
1293
+ // If we have an immediate load of this variable, the by-value
1294
+ // capture in a concurrent function will guarantee that this is
1295
+ // safe.
1296
+ if (parent.dyn_cast <LoadExpr *>())
1297
+ return false ;
1298
+
1299
+ // Otherwise, we have concurrent mutation. Complain.
1243
1300
ctx.Diags .diagnose (
1244
- loc, diag::concurrent_access_local,
1245
- value->getDescriptiveKind (), value->getName ());
1246
- value->diagnose (
1247
- diag::kind_declared_here, value->getDescriptiveKind ());
1301
+ loc, diag::concurrent_mutation_of_local_capture,
1302
+ var->getDescriptiveKind (), var->getName ());
1248
1303
return true ;
1249
1304
}
1250
1305
1251
- return false ;
1306
+ // Concurrent access to some other local.
1307
+ ctx.Diags .diagnose (
1308
+ loc, diag::concurrent_access_local,
1309
+ value->getDescriptiveKind (), value->getName ());
1310
+ value->diagnose (
1311
+ diag::kind_declared_here, value->getDescriptiveKind ());
1312
+ return true ;
1252
1313
1253
1314
case ActorIsolationRestriction::Unsafe:
1254
1315
return diagnoseReferenceToUnsafeGlobal (value, loc);
@@ -1535,6 +1596,10 @@ SourceLoc ConcurrentExecutionChecker::getConcurrentReferenceLoc(
1535
1596
enclosingBody = enclosingFunc->getBody ();
1536
1597
else if (auto enclosingClosure = dyn_cast<ClosureExpr>(enclosingDC))
1537
1598
enclosingBody = enclosingClosure->getBody ();
1599
+ else if (auto enclosingTopLevelCode = dyn_cast<TopLevelCodeDecl>(enclosingDC))
1600
+ enclosingBody = enclosingTopLevelCode->getBody ();
1601
+ else
1602
+ return SourceLoc ();
1538
1603
1539
1604
assert (enclosingBody && " Cannot have a local function here" );
1540
1605
ConcurrentLocalRefWalker walker (*this , localFunc);
@@ -1548,10 +1613,8 @@ bool ConcurrentExecutionChecker::mayExecuteConcurrentlyWith(
1548
1613
// Walk the context chain from the use to the definition.
1549
1614
while (useContext != defContext) {
1550
1615
// If we find a concurrent closure... it can be run concurrently.
1551
- // NOTE: We also classify escaping closures this way, which detects more
1552
- // problematic cases.
1553
1616
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
1554
- if (isEscapingClosure (closure) || isConcurrentClosure (closure))
1617
+ if (isConcurrentClosure (closure))
1555
1618
return true ;
1556
1619
}
1557
1620
0 commit comments