@@ -1001,11 +1001,13 @@ namespace {
1001
1001
1002
1002
ConcurrentExecutionChecker concurrentExecutionChecker;
1003
1003
1004
+ using MutableVarSource = llvm::PointerUnion<DeclRefExpr *, InOutExpr *>;
1004
1005
using MutableVarParent = llvm::PointerUnion<InOutExpr *, LoadExpr *>;
1005
1006
1006
- // / Mapping from mutable local variables to the parent expression, when
1007
- // / that parent is either a load or a inout expression.
1008
- llvm::SmallDenseMap<DeclRefExpr *, MutableVarParent, 4 > mutableLocalVarParent;
1007
+ // / Mapping from mutable local variables or inout expressions to the
1008
+ // / parent expression, when that parent is either a load or a inout expression.
1009
+ llvm::SmallDenseMap<MutableVarSource, MutableVarParent, 4 >
1010
+ mutableLocalVarParent;
1009
1011
1010
1012
const DeclContext *getDeclContext () const {
1011
1013
return contextStack.back ();
@@ -1022,28 +1024,66 @@ namespace {
1022
1024
// / If the subexpression is a reference to a mutable local variable from a
1023
1025
// / different context, record its parent. We'll query this as part of
1024
1026
// / capture semantics in concurrent functions.
1025
- void recordMutableVarParent (MutableVarParent parent, Expr *subExpr) {
1026
- auto declRef = dyn_cast<DeclRefExpr>(subExpr);
1027
- if (!declRef)
1028
- return ;
1027
+ // /
1028
+ // / \returns true if we recorded anything, false otherwise.
1029
+ bool recordMutableVarParent (MutableVarParent parent, Expr *subExpr) {
1030
+ subExpr = subExpr->getValueProvidingExpr ();
1031
+
1032
+ if (auto declRef = dyn_cast<DeclRefExpr>(subExpr)) {
1033
+ auto var = dyn_cast_or_null<VarDecl>(declRef->getDecl ());
1034
+ if (!var)
1035
+ return false ;
1029
1036
1030
- auto var = dyn_cast_or_null<VarDecl>(declRef-> getDecl ());
1031
- if (!var)
1032
- return ;
1037
+ // Only mutable variables matter.
1038
+ if (!var-> supportsMutation () )
1039
+ return false ;
1033
1040
1034
- // Only mutable variables matter.
1035
- if (!var->supportsMutation ())
1036
- return ;
1041
+ // Only mutable variables outside of the current context. This is an
1042
+ // optimization, because the parent map won't be queried in this case, and
1043
+ // it is the most common case for variables to be referenced in their
1044
+ // own context.
1045
+ if (var->getDeclContext () == getDeclContext ())
1046
+ return false ;
1037
1047
1038
- // Only mutable variables outside of the current context. This is an
1039
- // optimization, because the parent map won't be queried in this case, and
1040
- // it is the most common case for variables to be referenced in their
1041
- // own context.
1042
- if (var->getDeclContext () == getDeclContext ())
1043
- return ;
1048
+ assert (mutableLocalVarParent[declRef].isNull ());
1049
+ mutableLocalVarParent[declRef] = parent;
1050
+ return true ;
1051
+ }
1052
+
1053
+ // For a member reference, try to record a parent for the base
1054
+ // expression.
1055
+ if (auto memberRef = dyn_cast<MemberRefExpr>(subExpr)) {
1056
+ return recordMutableVarParent (parent, memberRef->getBase ());
1057
+ }
1058
+
1059
+ // For a subscript, try to record a parent for the base expression.
1060
+ if (auto subscript = dyn_cast<SubscriptExpr>(subExpr)) {
1061
+ return recordMutableVarParent (parent, subscript->getBase ());
1062
+ }
1044
1063
1045
- assert (mutableLocalVarParent[declRef].isNull ());
1046
- mutableLocalVarParent[declRef] = parent;
1064
+ // Look through postfix '!'.
1065
+ if (auto force = dyn_cast<ForceValueExpr>(subExpr)) {
1066
+ return recordMutableVarParent (parent, force->getSubExpr ());
1067
+ }
1068
+
1069
+ // Look through postfix '?'.
1070
+ if (auto bindOpt = dyn_cast<BindOptionalExpr>(subExpr)) {
1071
+ return recordMutableVarParent (parent, bindOpt->getSubExpr ());
1072
+ }
1073
+
1074
+ if (auto optEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
1075
+ return recordMutableVarParent (parent, optEval->getSubExpr ());
1076
+ }
1077
+
1078
+ // & expressions can be embedded for references to mutable variables
1079
+ // or subscribes inside a struct/enum.
1080
+ if (auto inout = dyn_cast<InOutExpr>(subExpr)) {
1081
+ // Record the parent of the inout so we don't look at it again later.
1082
+ mutableLocalVarParent[inout] = parent;
1083
+ return recordMutableVarParent (parent, inout->getSubExpr ());
1084
+ }
1085
+
1086
+ return false ;
1047
1087
}
1048
1088
1049
1089
public:
@@ -1127,7 +1167,8 @@ namespace {
1127
1167
if (!applyStack.empty ())
1128
1168
diagnoseInOutArg (applyStack.back (), inout, false );
1129
1169
1130
- recordMutableVarParent (inout, inout->getSubExpr ());
1170
+ if (mutableLocalVarParent.count (inout) == 0 )
1171
+ recordMutableVarParent (inout, inout->getSubExpr ());
1131
1172
}
1132
1173
1133
1174
if (auto load = dyn_cast<LoadExpr>(expr)) {
@@ -1225,6 +1266,9 @@ namespace {
1225
1266
if (auto *declRefExpr = dyn_cast<DeclRefExpr>(expr)) {
1226
1267
mutableLocalVarParent.erase (declRefExpr);
1227
1268
}
1269
+ if (auto *inoutExpr = dyn_cast<InOutExpr>(expr)) {
1270
+ mutableLocalVarParent.erase (inoutExpr);
1271
+ }
1228
1272
1229
1273
return expr;
1230
1274
}
0 commit comments