6363#include < set>
6464#include < sstream>
6565#include < string>
66+ #include < algorithm>
6667
6768using namespace swift ;
6869using namespace swift ::dependencies;
@@ -1244,47 +1245,129 @@ computeTransitiveClosureOfExplicitDependencies(
12441245 return result;
12451246}
12461247
1248+ static std::vector<ModuleDependencyID>
1249+ findClangDepPath (const ModuleDependencyID &from, const ModuleDependencyID &to,
1250+ ModuleDependenciesCache &cache) {
1251+ std::unordered_set<ModuleDependencyID> visited;
1252+ std::vector<ModuleDependencyID> result;
1253+ std::stack<ModuleDependencyID, std::vector<ModuleDependencyID>> stack;
1254+
1255+ // Must be explicitly-typed to allow recursion
1256+ std::function<void (const ModuleDependencyID &)> visit;
1257+
1258+ visit = [&visit, &cache, &visited, &result, &stack,
1259+ to](const ModuleDependencyID &moduleID) {
1260+ if (!visited.insert (moduleID).second )
1261+ return ;
1262+
1263+ if (moduleID == to) {
1264+ // Copy stack contents to the result
1265+ auto end = &stack.top () + 1 ;
1266+ auto begin = end - stack.size ();
1267+ result.assign (begin, end);
1268+ return ;
1269+ }
1270+
1271+ // Otherwise, visit each child node.
1272+ for (const auto &succID : cache.getAllDependencies (moduleID)) {
1273+ stack.push (succID);
1274+ visit (succID);
1275+ stack.pop ();
1276+ }
1277+ };
1278+
1279+ stack.push (from);
1280+ visit (from);
1281+ return result;
1282+ }
1283+
12471284static bool diagnoseCycle (CompilerInstance &instance,
12481285 ModuleDependenciesCache &cache,
12491286 ModuleDependencyID mainId) {
12501287 ModuleDependencyIDSetVector openSet;
12511288 ModuleDependencyIDSetVector closeSet;
1252- // Start from the main module.
1289+
1290+ auto kindIsSwiftDependency = [&](const ModuleDependencyID &ID) {
1291+ return ID.Kind == swift::ModuleDependencyKind::SwiftInterface ||
1292+ ID.Kind == swift::ModuleDependencyKind::SwiftBinary;
1293+ };
1294+
1295+ auto emitModulePath = [&](const std::vector<ModuleDependencyID> path,
1296+ llvm::SmallString<64 > &buffer) {
1297+ llvm::interleave (
1298+ path,
1299+ [&buffer](const ModuleDependencyID &id) {
1300+ buffer.append (id.ModuleName );
1301+ switch (id.Kind ) {
1302+ case swift::ModuleDependencyKind::SwiftInterface:
1303+ buffer.append (" .swiftinterface" );
1304+ break ;
1305+ case swift::ModuleDependencyKind::SwiftBinary:
1306+ buffer.append (" .swiftmodule" );
1307+ break ;
1308+ case swift::ModuleDependencyKind::Clang:
1309+ buffer.append (" .pcm" );
1310+ break ;
1311+ default :
1312+ llvm::report_fatal_error (
1313+ Twine (" Invalid Module Dependency Kind in cycle: " ) +
1314+ id.ModuleName );
1315+ break ;
1316+ }
1317+ },
1318+ [&buffer] { buffer.append (" -> " ); });
1319+ };
1320+
1321+ auto emitCycleDiagnostic = [&](const ModuleDependencyID &dep) {
1322+ auto startIt = std::find (openSet.begin (), openSet.end (), dep);
1323+ assert (startIt != openSet.end ());
1324+ std::vector cycleNodes (startIt, openSet.end ());
1325+ cycleNodes.push_back (*startIt);
1326+ llvm::SmallString<64 > errorBuffer;
1327+ emitModulePath (cycleNodes, errorBuffer);
1328+ instance.getASTContext ().Diags .diagnose (
1329+ SourceLoc (), diag::scanner_find_cycle, errorBuffer.str ());
1330+
1331+ // TODO: for (std::tuple<const ModuleDependencyID&, const
1332+ // ModuleDependencyID&> v : cycleNodes | std::views::adjacent<2>)
1333+ for (auto it = cycleNodes.begin (), end = cycleNodes.end (); it != end;
1334+ it++) {
1335+ if (it + 1 == cycleNodes.end ())
1336+ continue ;
1337+
1338+ const auto &thisID = *it;
1339+ const auto &nextID = *(it + 1 );
1340+ if (kindIsSwiftDependency (thisID) && kindIsSwiftDependency (nextID) &&
1341+ llvm::any_of (
1342+ cache.getOnlyOverlayDependencies (thisID),
1343+ [&](const ModuleDependencyID id) { return id == nextID; })) {
1344+ llvm::SmallString<64 > noteBuffer;
1345+ auto clangDepPath = findClangDepPath (
1346+ thisID,
1347+ ModuleDependencyID{nextID.ModuleName , ModuleDependencyKind::Clang},
1348+ cache);
1349+ emitModulePath (clangDepPath, noteBuffer);
1350+ instance.getASTContext ().Diags .diagnose (
1351+ SourceLoc (), diag::scanner_find_cycle_swift_overlay_path,
1352+ thisID.ModuleName , nextID.ModuleName , noteBuffer.str ());
1353+ }
1354+ }
1355+ };
1356+
1357+ // Start from the main module and check direct and overlay dependencies
12531358 openSet.insert (mainId);
12541359 while (!openSet.empty ()) {
1255- auto & lastOpen = openSet.back ();
1360+ auto lastOpen = openSet.back ();
12561361 auto beforeSize = openSet.size ();
12571362 assert (cache.findDependency (lastOpen).has_value () &&
12581363 " Missing dependency info during cycle diagnosis." );
1259-
12601364 for (const auto &dep : cache.getAllDependencies (lastOpen)) {
12611365 if (closeSet.count (dep))
12621366 continue ;
12631367 if (openSet.insert (dep)) {
12641368 break ;
12651369 } else {
1266- // Find a cycle, diagnose.
1267- auto startIt = std::find (openSet.begin (), openSet.end (), dep);
1268- assert (startIt != openSet.end ());
1269- llvm::SmallString<64 > buffer;
1270- for (auto it = startIt; it != openSet.end (); ++it) {
1271- buffer.append (it->ModuleName );
1272- buffer.append ((it->Kind == ModuleDependencyKind::SwiftInterface ||
1273- it->Kind == ModuleDependencyKind::SwiftSource ||
1274- it->Kind == ModuleDependencyKind::SwiftBinary)
1275- ? " .swiftmodule"
1276- : " .pcm" );
1277- buffer.append (" -> " );
1278- }
1279- buffer.append (startIt->ModuleName );
1280- buffer.append (
1281- (startIt->Kind == ModuleDependencyKind::SwiftInterface ||
1282- startIt->Kind == ModuleDependencyKind::SwiftSource ||
1283- startIt->Kind == ModuleDependencyKind::SwiftBinary)
1284- ? " .swiftmodule"
1285- : " .pcm" );
1286- instance.getASTContext ().Diags .diagnose (
1287- SourceLoc (), diag::scanner_find_cycle, buffer.str ());
1370+ emitCycleDiagnostic (dep);
12881371 return true ;
12891372 }
12901373 }
@@ -1297,6 +1380,7 @@ static bool diagnoseCycle(CompilerInstance &instance,
12971380 }
12981381 }
12991382 assert (openSet.empty ());
1383+ closeSet.clear ();
13001384 return false ;
13011385}
13021386
0 commit comments