85
85
#include " llvm/Support/YAMLTraits.h"
86
86
#include " llvm/Target/TargetMachine.h"
87
87
88
+ #include < algorithm>
88
89
#include < memory>
89
90
#include < unordered_set>
90
91
#include < utility>
@@ -351,22 +352,25 @@ class ABIDependencyEvaluator {
351
352
352
353
// / Check for cases where we have a fake cycle through an overlay.
353
354
// /
354
- // / \code
355
- // / Actual stack:
356
- // / sandwichedModule -> Overlay (Swift) -> ... -> sandwichedModule
357
- // / ^^--- wrong!
358
- // / Ideal stack:
359
- // / sandwichedModule -> Underlying (Clang)
360
- // / \endcode
355
+ // / Sometimes, we have fake cycles in the import graph due to the Clang
356
+ // / importer injecting overlays between Clang modules. These don't represent
357
+ // / an actual cycle in the build, so we should ignore them.
361
358
// /
362
- // / This happens when we have a dependency like:
363
- // / \code
364
- // / Overlay (Swift) -> sandwichedModule -> Underlying (Clang)
365
- // / \endcode
359
+ // / We check this lazily after detecting a cycle because it is difficult to
360
+ // / determine at the point where we see the overlay whether it was incorrectly
361
+ // / injected by the Clang importer or whether any of its imports will
362
+ // / eventually lead to a cycle.
363
+ // /
364
+ // / For more details, see [NOTE: ABIDependencyEvaluator-fake-cycle-detection]
366
365
// /
367
- // / We check this lazily because eagerly detecting if the dependency on an
368
- // / overlay is correct or not is difficult.
369
- bool isFakeCycleThroughOverlay (ModuleDecl **sandwichedModuleIter);
366
+ // / \param startOfCycle A pointer to the element of \c searchStack where
367
+ // / the module \em first appeared.
368
+ // /
369
+ // / \pre The module on top of \c searchStack is the same module as
370
+ // / *startOfCycle.
371
+ // /
372
+ // / \pre searchStack.begin() <= startOfCycle < searchStack.end()
373
+ bool isFakeCycleThroughOverlay (ModuleDecl **startOfCycle);
370
374
371
375
// / Recursive step in computing ABI dependencies.
372
376
// /
@@ -492,21 +496,87 @@ bool ABIDependencyEvaluator::isOverlayOfClangModule(ModuleDecl *swiftModule) {
492
496
return isOverlay;
493
497
}
494
498
499
+ // [NOTE: ABIDependencyEvaluator-fake-cycle-detection]
500
+ //
501
+ // First, let's consider a concrete example.
502
+ // - In Clang-land, ToyKit #imports CoreDoll.
503
+ // - The Swift overlay for CoreDoll imports both CoreDoll and ToyKit.
504
+ // Importing ToyKit from CoreDoll's overlay informally violates the layering
505
+ // of frameworks, but it doesn't actually create any cycles in the build
506
+ // dependencies.
507
+ // ┌───────────────────────────┐
508
+ // ┌───│ CoreDoll.swiftmodule │
509
+ // │ └───────────────────────────┘
510
+ // │ │
511
+ // import ToyKit @_exported import CoreDoll
512
+ // │ │
513
+ // │ │
514
+ // ▼ │
515
+ // ┌──────────────────────────┐ │
516
+ // │ ToyKit (ToyKit/ToyKit.h) │ │
517
+ // └──────────────────────────┘ │
518
+ // │ │
519
+ // #import <CoreDoll/CoreDoll.h> │
520
+ // │ │
521
+ // ▼ │
522
+ // ┌──────────────────────────────┐ │
523
+ // │CoreDoll (CoreDoll/CoreDoll.h)│◀──┘
524
+ // └──────────────────────────────┘
525
+ //
526
+ // Say we are trying to build a Swift module that imports ToyKit. Due to how
527
+ // module loading works, the Clang importer inserts the CoreDoll overlay
528
+ // between the ToyKit and CoreDoll Clang modules, creating a cycle in the
529
+ // import graph.
530
+ //
531
+ // ┌──────────────────────────┐
532
+ // │ ToyKit (ToyKit/ToyKit.h) │◀──────────┐
533
+ // └──────────────────────────┘ │
534
+ // │ │
535
+ // #import <CoreDoll/CoreDoll.h> import ToyKit
536
+ // │ │
537
+ // ▼ │
538
+ // ┌────────────────────────────┐ │
539
+ // │ CoreDoll.swiftmodule │─────────┘
540
+ // └────────────────────────────┘
541
+ // │
542
+ // @_exported import CoreDoll
543
+ // │
544
+ // ▼
545
+ // ┌──────────────────────────────┐
546
+ // │CoreDoll (CoreDoll/CoreDoll.h)│
547
+ // └──────────────────────────────┘
548
+ //
549
+ // This means that, at some point, searchStack will look like:
550
+ //
551
+ // [others] → ToyKit → CoreDoll (overlay) → ToyKit
552
+ //
553
+ // In the general case, there may be arbitrarily many modules in the cycle,
554
+ // including submodules.
555
+ //
556
+ // [others] → ToyKit → [others] → CoreDoll (overlay) → [others] → ToyKit
557
+ //
558
+ // where "[others]" indicates 0 or more modules of any kind.
559
+ //
560
+ // To detect this, we check that the start of the cycle is a Clang module and
561
+ // that there is at least one overlay between it and its recurrence at the end
562
+ // of the searchStack. If so, we assume we have detected a benign cycle which
563
+ // can be safely ignored.
564
+
495
565
bool ABIDependencyEvaluator::isFakeCycleThroughOverlay (
496
- ModuleDecl **sandwichModuleIter) {
497
- assert (sandwichModuleIter >= searchStack.begin ()
498
- && sandwichModuleIter < searchStack.end ()
499
- && " sandwichModuleIter points to an element in searchStack" );
500
- // The sandwichedModule must be a Clang module.
501
- if (!(*sandwichModuleIter)->isNonSwiftModule ())
502
- return false ;
503
- auto nextModuleIter = sandwichModuleIter + 1 ;
504
- if (nextModuleIter == searchStack.end ())
505
- return false ;
506
- // The next module must be a Swift overlay for a Clang module
507
- if ((*nextModuleIter)->isNonSwiftModule ())
566
+ ModuleDecl **startOfCycle) {
567
+ assert (startOfCycle >= searchStack.begin () &&
568
+ startOfCycle < searchStack.end () &&
569
+ " startOfCycleIter points to an element in searchStack" );
570
+ // The startOfCycle module must be a Clang module.
571
+ if (!(*startOfCycle)->isNonSwiftModule ())
508
572
return false ;
509
- return isOverlayOfClangModule (*nextModuleIter);
573
+ // Next, we must have zero or more modules followed by a Swift overlay for a
574
+ // Clang module.
575
+ return std::any_of (startOfCycle + 1 , searchStack.end (),
576
+ [this ](ModuleDecl *module ) {
577
+ return !module ->isNonSwiftModule () &&
578
+ isOverlayOfClangModule (module );
579
+ });
510
580
}
511
581
512
582
void ABIDependencyEvaluator::computeABIDependenciesForModule (
@@ -518,7 +588,11 @@ void ABIDependencyEvaluator::computeABIDependenciesForModule(
518
588
crashOnInvariantViolation ([&](llvm::raw_string_ostream &os) {
519
589
os << " unexpected cycle in import graph!\n " ;
520
590
for (auto m: searchStack) {
521
- printModule (m, os); os << " \n depends on " ;
591
+ printModule (m, os);
592
+ if (!m->isNonSwiftModule ()) {
593
+ os << " (isOverlay = " << isOverlayOfClangModule (m) << " )" ;
594
+ }
595
+ os << " \n depends on " ;
522
596
}
523
597
printModule (module , os); os << ' \n ' ;
524
598
});
0 commit comments