@@ -163,26 +163,75 @@ class AccessedStorage {
163
163
}
164
164
};
165
165
166
+ enum class RecordedAccessKind {
167
+ // / The access was for a 'begin_access' instruction in the current function
168
+ // / being checked.
169
+ BeginInstruction,
170
+
171
+ // / The access was inside noescape closure that we either
172
+ // / passed to function or called directly. It results from applying the
173
+ // / the summary of the closure to the closure's captures.
174
+ NoescapeClosureCapture
175
+ };
176
+
166
177
// / Records an access to an address and the single subpath of projections
167
178
// / that was performed on the address, if such a single subpath exists.
168
179
class RecordedAccess {
169
180
private:
170
- BeginAccessInst *Inst;
171
- const IndexTrieNode *SubPath;
181
+ RecordedAccessKind RecordKind;
182
+ union {
183
+ BeginAccessInst *Inst;
184
+ struct {
185
+ SILAccessKind ClosureAccessKind;
186
+ SILLocation ClosureAccessLoc;
187
+ };
188
+ };
172
189
190
+ const IndexTrieNode *SubPath;
173
191
public:
174
- RecordedAccess (BeginAccessInst *BAI, const IndexTrieNode *SubPath)
175
- : Inst(BAI), SubPath(SubPath) {}
192
+ RecordedAccess (BeginAccessInst *BAI, const IndexTrieNode *SubPath) :
193
+ RecordKind (RecordedAccessKind::BeginInstruction), Inst(BAI),
194
+ SubPath (SubPath) { }
195
+
196
+ RecordedAccess (SILAccessKind ClosureAccessKind,
197
+ SILLocation ClosureAccessLoc, const IndexTrieNode *SubPath) :
198
+ RecordKind (RecordedAccessKind::NoescapeClosureCapture),
199
+ ClosureAccessKind (ClosureAccessKind), ClosureAccessLoc(ClosureAccessLoc),
200
+ SubPath (SubPath) { }
201
+
202
+ RecordedAccessKind getRecordKind () const {
203
+ return RecordKind;
204
+ }
176
205
177
- BeginAccessInst *getInstruction () const { return Inst; }
206
+ BeginAccessInst *getInstruction () const {
207
+ assert (RecordKind == RecordedAccessKind::BeginInstruction);
208
+ return Inst;
209
+ }
178
210
179
- SILAccessKind getAccessKind () const { return Inst->getAccessKind (); }
211
+ SILAccessKind getAccessKind () const {
212
+ switch (RecordKind) {
213
+ case RecordedAccessKind::BeginInstruction:
214
+ return Inst->getAccessKind ();
215
+ case RecordedAccessKind::NoescapeClosureCapture:
216
+ return ClosureAccessKind;
217
+ };
218
+ }
180
219
181
- SILLocation getAccessLoc () const { return Inst->getLoc (); }
220
+ SILLocation getAccessLoc () const {
221
+ switch (RecordKind) {
222
+ case RecordedAccessKind::BeginInstruction:
223
+ return Inst->getLoc ();
224
+ case RecordedAccessKind::NoescapeClosureCapture:
225
+ return ClosureAccessLoc;
226
+ };
227
+ }
182
228
183
- const IndexTrieNode *getSubPath () const { return SubPath; }
229
+ const IndexTrieNode *getSubPath () const {
230
+ return SubPath;
231
+ }
184
232
};
185
233
234
+
186
235
// / Records the in-progress accesses to a given sub path.
187
236
class SubAccessInfo {
188
237
public:
@@ -676,9 +725,11 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation,
676
725
diagnose (Ctx, MainAccess.getAccessLoc ().getSourceLoc (), DiagnosticID,
677
726
PathDescription, AccessKindForMain);
678
727
D.highlight (RangeForMain);
679
- tryFixItWithCallToCollectionSwapAt (FirstAccess.getInstruction (),
680
- SecondAccess.getInstruction (),
681
- CallsToSwap, Ctx, D);
728
+ if (SecondAccess.getRecordKind () == RecordedAccessKind::BeginInstruction) {
729
+ tryFixItWithCallToCollectionSwapAt (FirstAccess.getInstruction (),
730
+ SecondAccess.getInstruction (),
731
+ CallsToSwap, Ctx, D);
732
+ }
682
733
} else {
683
734
auto DiagnosticID = (Ctx.LangOpts .isSwiftVersion3 () ?
684
735
diag::exclusivity_access_required_unknown_decl_swift3 :
@@ -868,6 +919,102 @@ Optional<RecordedAccess> shouldReportAccess(const AccessInfo &Info,
868
919
return Info.conflictsWithAccess (Kind, SubPath);
869
920
}
870
921
922
+ // / Use the summary analysis to check whether a call to the given
923
+ // / function would conflict with any in progress accesses. The starting
924
+ // / index indicates what index into the the callee's parameters the
925
+ // / arguments array starts at -- this is useful for partial_apply functions,
926
+ // / which pass only a suffix of the callee's arguments at the apply site.
927
+ static void checkForViolationWithCall (
928
+ const StorageMap &Accesses, SILFunction *Callee, unsigned StartingAtIndex,
929
+ OperandValueArrayRef Arguments, AccessSummaryAnalysis *ASA,
930
+ llvm::SmallVectorImpl<ConflictingAccess> &ConflictingAccesses) {
931
+ const AccessSummaryAnalysis::FunctionSummary &FS =
932
+ ASA->getOrCreateSummary (Callee);
933
+
934
+ // For each argument in the suffix of the callee arguments being passed
935
+ // at this call site, determine whether the arguments will be accessed
936
+ // in a way that conflicts with any currently in progress accesses.
937
+ // If so, diagnose.
938
+ for (unsigned ArgumentIndex : indices (Arguments)) {
939
+ unsigned CalleeIndex = StartingAtIndex + ArgumentIndex;
940
+
941
+ const AccessSummaryAnalysis::ArgumentSummary &AS =
942
+ FS.getAccessForArgument (CalleeIndex);
943
+ Optional<SILAccessKind> Kind = AS.getAccessKind ();
944
+ if (!Kind)
945
+ continue ;
946
+
947
+ SILValue Argument = Arguments[ArgumentIndex];
948
+ assert (Argument->getType ().isAddress ());
949
+
950
+ const AccessedStorage &Storage = findAccessedStorage (Argument);
951
+ auto AccessIt = Accesses.find (Storage);
952
+ if (AccessIt == Accesses.end ())
953
+ continue ;
954
+ const AccessInfo &Info = AccessIt->getSecond ();
955
+
956
+ // TODO: For now, treat a summarized access as an access to the whole
957
+ // address. Once the summary analysis is sensitive to stored properties,
958
+ // this should be updated look at the subpaths from the summary.
959
+ const IndexTrieNode *SubPath = ASA->getSubPathTrieRoot ();
960
+ if (auto Conflict = shouldReportAccess (Info, *Kind, SubPath)) {
961
+ SILLocation AccessLoc = AS.getAccessLoc ();
962
+ const auto &SecondAccess = RecordedAccess (*Kind, AccessLoc, SubPath);
963
+ ConflictingAccesses.emplace_back (Storage, *Conflict, SecondAccess);
964
+ }
965
+ }
966
+ }
967
+
968
+ // / Look through a value passed as a function argument to determine whether
969
+ // / it is a partial_apply.
970
+ static PartialApplyInst *lookThroughForPartialApply (SILValue V) {
971
+ while (true ) {
972
+ if (auto CFI = dyn_cast<ConvertFunctionInst>(V)) {
973
+ V = CFI->getOperand ();
974
+ continue ;
975
+ }
976
+
977
+ if (auto *PAI = dyn_cast<PartialApplyInst>(V))
978
+ return PAI;
979
+
980
+ return nullptr ;
981
+ }
982
+ }
983
+
984
+ // / Checks whether any of the arguments to the apply are closures and diagnoses
985
+ // / if any of the @inout_aliasable captures passed to those closures have
986
+ // / in-progress accesses that would conflict with any access the summary
987
+ // / says the closure would perform.
988
+ static void checkForViolationsInNoEscapeClosures (
989
+ const StorageMap &Accesses, FullApplySite FAS, AccessSummaryAnalysis *ASA,
990
+ llvm::SmallVectorImpl<ConflictingAccess> &ConflictingAccesses) {
991
+
992
+ SILFunction *Callee = FAS.getCalleeFunction ();
993
+ if (Callee && !Callee->empty ()) {
994
+ // Check for violation with directly called closure
995
+ checkForViolationWithCall (Accesses, Callee, 0 , FAS.getArguments (), ASA,
996
+ ConflictingAccesses);
997
+ }
998
+
999
+ // Check for violation with closures passed as arguments
1000
+ for (SILValue Argument : FAS.getArguments ()) {
1001
+ auto *PAI = lookThroughForPartialApply (Argument);
1002
+ if (!PAI)
1003
+ continue ;
1004
+
1005
+ SILFunction *Closure = PAI->getCalleeFunction ();
1006
+ if (!Closure || Closure->empty ())
1007
+ continue ;
1008
+
1009
+ // Check the closure's captures, which are a suffix of the closure's
1010
+ // parameters.
1011
+ unsigned StartIndex =
1012
+ Closure->getArguments ().size () - PAI->getNumCallArguments ();
1013
+ checkForViolationWithCall (Accesses, Closure, StartIndex,
1014
+ PAI->getArguments (), ASA, ConflictingAccesses);
1015
+ }
1016
+ }
1017
+
871
1018
static void checkStaticExclusivity (SILFunction &Fn, PostOrderFunctionInfo *PO,
872
1019
AccessSummaryAnalysis *ASA) {
873
1020
// The implementation relies on the following SIL invariants:
@@ -966,8 +1113,17 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO,
966
1113
// Record calls to swap() for potential Fix-Its.
967
1114
if (isCallToStandardLibrarySwap (AI, Fn.getASTContext ()))
968
1115
CallsToSwap.push_back (AI);
1116
+ else
1117
+ checkForViolationsInNoEscapeClosures (Accesses, AI, ASA,
1118
+ ConflictingAccesses);
1119
+ continue ;
969
1120
}
970
1121
1122
+ if (auto *TAI = dyn_cast<TryApplyInst>(&I)) {
1123
+ checkForViolationsInNoEscapeClosures (Accesses, TAI, ASA,
1124
+ ConflictingAccesses);
1125
+ continue ;
1126
+ }
971
1127
// Sanity check to make sure entries are properly removed.
972
1128
assert ((!isa<ReturnInst>(&I) || Accesses.size () == 0 ) &&
973
1129
" Entries were not properly removed?!" );
0 commit comments