[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
This commit is contained in:
Artem Chikin
2025-10-15 16:58:50 -07:00
parent cb4cb70e93
commit d699e2fc49
5 changed files with 48 additions and 4 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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