From d699e2fc49cc71d0acbf03f9096718a9e30e401c Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 15 Oct 2025 16:58:50 -0700 Subject: [PATCH] [Dependency Scanning] Warn, instead of fail, when a Swift dependency query only finds modules built for incompatible target We have adopters who are relying on directly importing the underlying Clang module in the presence of incompatible Swift modules. Resolves rdar://162549210 --- include/swift/Serialization/ScanningLoaders.h | 5 ++++ .../ModuleDependencyScanner.cpp | 13 ++++++++++ lib/Serialization/ScanningLoaders.cpp | 2 +- .../incompatible-arch-underlying-clang.swift | 25 +++++++++++++++++++ test/ScanDependencies/incompatible-arch.swift | 7 +++--- 5 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 test/ScanDependencies/incompatible-arch-underlying-clang.swift diff --git a/include/swift/Serialization/ScanningLoaders.h b/include/swift/Serialization/ScanningLoaders.h index 709ade4521666..f7fc458af705f 100644 --- a/include/swift/Serialization/ScanningLoaders.h +++ b/include/swift/Serialization/ScanningLoaders.h @@ -22,6 +22,11 @@ namespace swift { /// Result of looking up a Swift module on the current filesystem /// search paths. struct SwiftModuleScannerQueryResult { + // Checked for by the scanner as a special case + // for downgrading imcompatible-candidate-only lookup result + // to a warning. + static constexpr const char *BUILT_FOR_INCOMPATIBLE_TARGET = + "built for incompatible target"; struct IncompatibleCandidate { std::string path; std::string incompatibilityReason; diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 5430adb2e940a..69623c63dfc18 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -2088,6 +2088,19 @@ void ModuleDependencyIssueReporter::diagnoseFailureOnOnlyIncompatibleCandidates( if (candidates.empty()) return; + // FIXME: There are known cases where clients are relying on + // loading the underlying Clang module in the presence of a Swift + // module which is lacking the required target-specific variant, + // such as MacCatalyst. Eventually, we should pursue making this + // an error as well. + if (llvm::all_of(candidates, [](auto &incompatibleCandidate) { + return incompatibleCandidate.incompatibilityReason == + SwiftModuleScannerQueryResult::BUILT_FOR_INCOMPATIBLE_TARGET; + })) { + warnOnIncompatibleCandidates(moduleImport.importIdentifier, candidates); + return; + } + diagnoseModuleNotFoundFailure(moduleImport, cache, dependencyOf, /* resolvingSerializedSearchPath */ std::nullopt, candidates); diff --git a/lib/Serialization/ScanningLoaders.cpp b/lib/Serialization/ScanningLoaders.cpp index a3f9f023d8bcd..8b2eb6ee9deef 100644 --- a/lib/Serialization/ScanningLoaders.cpp +++ b/lib/Serialization/ScanningLoaders.cpp @@ -117,7 +117,7 @@ bool SwiftModuleScanner::handlePossibleTargetMismatch( for (const auto &modulePath : foundIncompatibleArchModules) incompatibleCandidates.push_back({modulePath, - "invalid architecture"}); + SwiftModuleScannerQueryResult::BUILT_FOR_INCOMPATIBLE_TARGET}); return false; } diff --git a/test/ScanDependencies/incompatible-arch-underlying-clang.swift b/test/ScanDependencies/incompatible-arch-underlying-clang.swift new file mode 100644 index 0000000000000..582abcc06e3d6 --- /dev/null +++ b/test/ScanDependencies/incompatible-arch-underlying-clang.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/deps) +// RUN: %empty-directory(%t/module-cache) +// RUN: %empty-directory(%t/inputs/Foo.swiftmodule) +// RUN: touch %t/inputs/Foo.swiftmodule/i387.swiftmodule +// RUN: touch %t/inputs/Foo.swiftmodule/ppc65.swiftmodule +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %t/client.swift -o %t/deps.json -I %t/inputs -I %t/deps -diagnostic-style llvm -scanner-module-validation 2>&1 | %FileCheck %s + +// CHECK-DAG: warning: module file '{{.*}}Foo.swiftmodule{{/|\\}}ppc65.swiftmodule' is incompatible with this Swift compiler: built for incompatible target +// CHECK-DAG: warning: module file '{{.*}}Foo.swiftmodule{{/|\\}}i387.swiftmodule' is incompatible with this Swift compiler: built for incompatible target +// CHECK-NOT: error + +//--- deps/foo.h +void foo(void); + +//--- deps/module.modulemap +module Foo { + header "foo.h" + export * +} + +//--- client.swift +import Foo diff --git a/test/ScanDependencies/incompatible-arch.swift b/test/ScanDependencies/incompatible-arch.swift index 58cc7ec916fa4..9894ed426c9b5 100644 --- a/test/ScanDependencies/incompatible-arch.swift +++ b/test/ScanDependencies/incompatible-arch.swift @@ -6,7 +6,8 @@ // RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %s -o %t/deps.json -I %t/inputs -diagnostic-style llvm -scanner-module-validation 2>&1 | %FileCheck %s -// CHECK: error: unable to resolve Swift module dependency to a compatible module: 'Foo' -// CHECK-DAG: note: found incompatible module '{{.*}}Foo.swiftmodule{{/|\\}}ppc65.swiftmodule': invalid architecture -// CHECK-DAG: note: found incompatible module '{{.*}}Foo.swiftmodule{{/|\\}}i387.swiftmodule': invalid architecture +// CHECK-DAG: warning: module file '{{.*}}Foo.swiftmodule{{/|\\}}ppc65.swiftmodule' is incompatible with this Swift compiler: built for incompatible target +// CHECK-DAG: warning: module file '{{.*}}Foo.swiftmodule{{/|\\}}i387.swiftmodule' is incompatible with this Swift compiler: built for incompatible target +// CHECK: error: unable to resolve module dependency: 'Foo' + import Foo