@@ -133,8 +133,7 @@ RequirementMachine *RewriteContext::getRequirementMachine(
133
133
auto *newMachine = new rewriting::RequirementMachine (*this );
134
134
machine = newMachine;
135
135
136
- // This might re-entrantly invalidate 'machine', which is a reference
137
- // into Protos.
136
+ // This might re-entrantly invalidate 'machine'.
138
137
auto status = newMachine->initWithGenericSignature (sig);
139
138
newMachine->checkCompletionResult (status.first );
140
139
@@ -170,7 +169,10 @@ void RewriteContext::getProtocolComponentRec(
170
169
stack.push_back (proto);
171
170
172
171
// Look at each successor.
173
- for (auto *depProto : proto->getProtocolDependencies ()) {
172
+ auto found = Dependencies.find (proto);
173
+ assert (found != Dependencies.end ());
174
+
175
+ for (auto *depProto : found->second ) {
174
176
auto found = Protos.find (depProto);
175
177
if (found == Protos.end ()) {
176
178
// Successor has not yet been visited. Recurse.
@@ -223,40 +225,121 @@ void RewriteContext::getProtocolComponentRec(
223
225
}
224
226
}
225
227
226
- // / Lazily construct a requirement machine for the given protocol's strongly
227
- // / connected component (SCC) in the protocol dependency graph .
228
+ // / Get the strongly connected component (SCC) of the protocol dependency
229
+ // / graph containing the given protocol .
228
230
// /
229
- // / This can only be called once, to prevent multiple requirement machines
230
- // / for being built with the same component.
231
- ArrayRef<const ProtocolDecl *> RewriteContext::getProtocolComponent (
232
- const ProtocolDecl *proto) {
231
+ // / You must not hold on to this reference across calls to any other
232
+ // / Requirement Machine operations, since they might insert new entries
233
+ // / into the underlying DenseMap, invalidating the reference.
234
+ RewriteContext::ProtocolComponent &
235
+ RewriteContext::getProtocolComponentImpl (const ProtocolDecl *proto) {
236
+ {
237
+ // We pre-load protocol dependencies into the Dependencies map
238
+ // because getProtocolDependencies() can trigger recursive calls into
239
+ // the requirement machine in highly-invalid code, which violates
240
+ // invariants in getProtocolComponentRec().
241
+ SmallVector<const ProtocolDecl *, 3 > worklist;
242
+ worklist.push_back (proto);
243
+
244
+ while (!worklist.empty ()) {
245
+ const auto *otherProto = worklist.back ();
246
+ worklist.pop_back ();
247
+
248
+ auto found = Dependencies.find (otherProto);
249
+ if (found != Dependencies.end ())
250
+ continue ;
251
+
252
+ auto protoDeps = otherProto->getProtocolDependencies ();
253
+ Dependencies.insert (std::make_pair (otherProto, protoDeps));
254
+ for (auto *nextProto : protoDeps)
255
+ worklist.push_back (nextProto);
256
+ }
257
+ }
258
+
233
259
auto found = Protos.find (proto);
234
260
if (found == Protos.end ()) {
261
+ if (ProtectProtocolComponentRec) {
262
+ llvm::errs () << " Too much recursion is bad\n " ;
263
+ abort ();
264
+ }
265
+
266
+ ProtectProtocolComponentRec = true ;
267
+
235
268
SmallVector<const ProtocolDecl *, 3 > stack;
236
269
getProtocolComponentRec (proto, stack);
237
270
assert (stack.empty ());
238
271
239
272
found = Protos.find (proto);
240
273
assert (found != Protos.end ());
274
+
275
+ ProtectProtocolComponentRec = false ;
241
276
}
242
277
243
278
assert (Components.count (found->second .ComponentID ) != 0 );
244
279
auto &component = Components[found->second .ComponentID ];
245
280
246
- if (component.InProgress ) {
247
- llvm::errs () << " Re-entrant construction of requirement "
248
- << " machine for:" ;
281
+ assert (std::find (component.Protos .begin (), component.Protos .end (), proto)
282
+ != component.Protos .end () && " Protocol is in the wrong SCC" );
283
+ return component;
284
+ }
285
+
286
+ // / Get the list of protocols in the strongly connected component (SCC)
287
+ // / of the protocol dependency graph containing the given protocol.
288
+ // /
289
+ // / This can only be called once, to prevent multiple requirement machines
290
+ // / for being built with the same component.
291
+ ArrayRef<const ProtocolDecl *> RewriteContext::getProtocolComponent (
292
+ const ProtocolDecl *proto) {
293
+ auto &component = getProtocolComponentImpl (proto);
294
+
295
+ if (component.ComputingRequirementSignatures ) {
296
+ llvm::errs () << " Re-entrant minimization of requirement signatures for: " ;
249
297
for (auto *proto : component.Protos )
250
298
llvm::errs () << " " << proto->getName ();
251
299
llvm::errs () << " \n " ;
252
300
abort ();
253
301
}
254
302
255
- component.InProgress = true ;
303
+ component.ComputingRequirementSignatures = true ;
256
304
257
305
return component.Protos ;
258
306
}
259
307
308
+ // / Get the list of protocols in the strongly connected component (SCC)
309
+ // / of the protocol dependency graph containing the given protocol.
310
+ // /
311
+ // / This can only be called once, to prevent multiple requirement machines
312
+ // / for being built with the same component.
313
+ RequirementMachine *RewriteContext::getRequirementMachine (
314
+ const ProtocolDecl *proto) {
315
+ auto &component = getProtocolComponentImpl (proto);
316
+
317
+ if (component.Machine ) {
318
+ if (!component.Machine ->isComplete ()) {
319
+ llvm::errs () << " Re-entrant construction of requirement machine for: " ;
320
+ for (auto *proto : component.Protos )
321
+ llvm::errs () << " " << proto->getName ();
322
+ llvm::errs () << " \n " ;
323
+ abort ();
324
+ }
325
+
326
+ return component.Machine ;
327
+ }
328
+
329
+ // Store this requirement machine before adding the protocols, to catch
330
+ // re-entrant construction via initWithProtocolSignatureRequirements()
331
+ // below.
332
+ auto *newMachine = new rewriting::RequirementMachine (*this );
333
+ component.Machine = newMachine;
334
+
335
+ // This might re-entrantly invalidate 'component.Machine'.
336
+ auto status = newMachine->initWithProtocolSignatureRequirements (
337
+ component.Protos );
338
+ newMachine->checkCompletionResult (status.first );
339
+
340
+ return newMachine;
341
+ }
342
+
260
343
bool RewriteContext::isRecursivelyConstructingRequirementMachine (
261
344
const ProtocolDecl *proto) {
262
345
if (proto->isRequirementSignatureComputed ())
@@ -270,12 +353,17 @@ bool RewriteContext::isRecursivelyConstructingRequirementMachine(
270
353
if (component == Components.end ())
271
354
return false ;
272
355
273
- return component->second .InProgress ;
356
+ return component->second .ComputingRequirementSignatures ;
274
357
}
275
358
276
359
// / We print stats in the destructor, which should get executed at the end of
277
360
// / a compilation job.
278
361
RewriteContext::~RewriteContext () {
362
+ for (const auto &pair : Components)
363
+ delete pair.second .Machine ;
364
+
365
+ Components.clear ();
366
+
279
367
for (const auto &pair : Machines)
280
368
delete pair.second ;
281
369
0 commit comments