19
19
#define SWIFT_AST_EVALUATOR_H
20
20
21
21
#include " swift/AST/AnyRequest.h"
22
+ #include " swift/AST/EvaluatorDependencies.h"
22
23
#include " swift/Basic/AnyValue.h"
23
24
#include " swift/Basic/Debug.h"
24
25
#include " swift/Basic/Defer.h"
@@ -47,6 +48,12 @@ class DiagnosticEngine;
47
48
class Evaluator ;
48
49
class UnifiedStatsReporter ;
49
50
51
+ namespace detail {
52
+ // Remove this when the compiler bumps to C++17.
53
+ template <typename ...>
54
+ using void_t = void ;
55
+ }
56
+
50
57
// / An "abstract" request function pointer, which is the storage type
51
58
// / used for each of the
52
59
using AbstractRequestFunction = void (void );
@@ -224,6 +231,38 @@ class Evaluator {
224
231
// / so all clients must cope with cycles.
225
232
llvm::DenseMap<AnyRequest, std::vector<AnyRequest>> dependencies;
226
233
234
+ // / A stack of dependency sources in the order they were evaluated.
235
+ llvm::SmallVector<evaluator::DependencySource, 8 > dependencySources;
236
+
237
+ // / An RAII type that manages manipulating the evaluator's
238
+ // / dependency source stack. It is specialized to be zero-cost for
239
+ // / requests that are not dependency sources.
240
+ template <typename Request, typename = detail::void_t <>>
241
+ struct IncrementalDependencyStackRAII {
242
+ IncrementalDependencyStackRAII (Evaluator &E, const Request &Req) {}
243
+ };
244
+
245
+ template <typename Request>
246
+ struct IncrementalDependencyStackRAII <
247
+ Request, typename std::enable_if<Request::isDependencySource>::type> {
248
+ NullablePtr<Evaluator> Eval;
249
+ IncrementalDependencyStackRAII (Evaluator &E, const Request &Req) {
250
+ auto Source = Req.readDependencySource (E);
251
+ // If there is no source to introduce, bail. This can occur if
252
+ // a request originates in the context of a module.
253
+ if (!Source.getPointer ()) {
254
+ return ;
255
+ }
256
+ E.dependencySources .emplace_back (Source);
257
+ Eval = &E;
258
+ }
259
+
260
+ ~IncrementalDependencyStackRAII () {
261
+ if (Eval.isNonNull ())
262
+ Eval.get ()->dependencySources .pop_back ();
263
+ }
264
+ };
265
+
227
266
// / Retrieve the request function for the given zone and request IDs.
228
267
AbstractRequestFunction *getAbstractRequestFunction (uint8_t zoneID,
229
268
uint8_t requestID) const ;
@@ -264,6 +303,7 @@ class Evaluator {
264
303
typename std::enable_if<Request::isEverCached>::type * = nullptr >
265
304
llvm::Expected<typename Request::OutputType>
266
305
operator ()(const Request &request) {
306
+ IncrementalDependencyStackRAII<Request> incDeps{*this , request};
267
307
// The request can be cached, but check a predicate to determine
268
308
// whether this particular instance is cached. This allows more
269
309
// fine-grained control over which instances get cache.
@@ -279,6 +319,7 @@ class Evaluator {
279
319
typename std::enable_if<!Request::isEverCached>::type * = nullptr >
280
320
llvm::Expected<typename Request::OutputType>
281
321
operator ()(const Request &request) {
322
+ IncrementalDependencyStackRAII<Request> incDeps{*this , request};
282
323
return getResultUncached (request);
283
324
}
284
325
@@ -366,7 +407,9 @@ class Evaluator {
366
407
FrontendStatsTracer statsTracer = make_tracer (stats, request);
367
408
if (stats) reportEvaluatedRequest (*stats, request);
368
409
369
- return getRequestFunction<Request>()(request, *this );
410
+ auto &&r = getRequestFunction<Request>()(request, *this );
411
+ reportEvaluatedResult<Request>(request, r);
412
+ return std::move (r);
370
413
}
371
414
372
415
// / Get the result of a request, consulting an external cache
@@ -377,8 +420,10 @@ class Evaluator {
377
420
llvm::Expected<typename Request::OutputType>
378
421
getResultCached (const Request &request) {
379
422
// If there is a cached result, return it.
380
- if (auto cached = request.getCachedResult ())
423
+ if (auto cached = request.getCachedResult ()) {
424
+ reportEvaluatedResult<Request>(request, *cached);
381
425
return *cached;
426
+ }
382
427
383
428
// Compute the result.
384
429
auto result = getResultUncached (request);
@@ -403,7 +448,9 @@ class Evaluator {
403
448
// If we already have an entry for this request in the cache, return it.
404
449
auto known = cache.find_as (request);
405
450
if (known != cache.end ()) {
406
- return known->second .template castTo <typename Request::OutputType>();
451
+ auto r = known->second .template castTo <typename Request::OutputType>();
452
+ reportEvaluatedResult<Request>(request, r);
453
+ return r;
407
454
}
408
455
409
456
// Compute the result.
@@ -416,6 +463,53 @@ class Evaluator {
416
463
return result;
417
464
}
418
465
466
+ private:
467
+ template <typename Request,
468
+ typename std::enable_if<!Request::isDependencySink>::type * = nullptr >
469
+ void reportEvaluatedResult (const Request &r,
470
+ const typename Request::OutputType &o) const {}
471
+
472
+ template <typename Request,
473
+ typename std::enable_if<Request::isDependencySink>::type * = nullptr >
474
+ void reportEvaluatedResult (const Request &r,
475
+ const typename Request::OutputType &o) {
476
+ r.writeDependencySink (*this , o);
477
+ }
478
+
479
+ public:
480
+ // / Returns \c true if the scope of the current active source cascades.
481
+ // /
482
+ // / If there is no active scope, the result always cascades.
483
+ bool isActiveSourceCascading () const {
484
+ return getActiveSourceScope () == evaluator::DependencyScope::Cascading;
485
+ }
486
+
487
+ // / Returns the scope of the current active scope.
488
+ // /
489
+ // / If there is no active scope, the result always cascades.
490
+ evaluator::DependencyScope getActiveSourceScope () const {
491
+ if (dependencySources.empty ()) {
492
+ return evaluator::DependencyScope::Cascading;
493
+ }
494
+ return dependencySources.back ().getInt ();
495
+ }
496
+
497
+ // / Returns the active dependency's source file, or \c nullptr if no
498
+ // / dependency source is active.
499
+ SourceFile *getActiveDependencySource () const {
500
+ if (dependencySources.empty ())
501
+ return nullptr ;
502
+ return dependencySources.back ().getPointer ();
503
+ }
504
+
505
+ // / If there is an active dependency source, returns its
506
+ // / \c ReferencedNameTracker. Else, returns \c nullptr.
507
+ ReferencedNameTracker *getActiveDependencyTracker () const {
508
+ if (auto *source = getActiveDependencySource ())
509
+ return source->getRequestBasedReferencedNameTracker ();
510
+ return nullptr ;
511
+ }
512
+
419
513
public:
420
514
// / Print the dependencies of the given request as a tree.
421
515
// /
0 commit comments