From 977a1e1fe4a2b635a3a842afc855dae90fe6f88d Mon Sep 17 00:00:00 2001 From: Cyndy Ishida Date: Thu, 12 Jun 2025 16:19:24 -0700 Subject: [PATCH] [Modules] Allow implicit conversions when loading interfaces with invalid os versions. Initially, the compiler rejected building dependencies that contained OS versions in an invalid range. This happens to be quite disruptive, so instead allow it and request that these versions be implicitly bumped based on what `llvm::getCanonicalVersionForOS` computes --- include/swift/AST/DiagnosticsSema.def | 4 -- include/swift/Basic/Platform.h | 10 ++++ lib/Basic/Platform.cpp | 31 +++++++++++ lib/ClangImporter/ClangImporter.cpp | 16 ++++++ lib/Serialization/SerializedModuleLoader.cpp | 48 ++++++----------- .../canonicalized-os-version.swift | 53 +++++++++++++++++++ 6 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 test/ModuleInterface/canonicalized-os-version.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8aed91cf2d1ef..f12bd7a6c03cf 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -895,10 +895,6 @@ ERROR(map_os_version_from_textual_interface_failed,none, "failed to map OS version from %0 to %1 in %2", (StringRef, StringRef, StringRef)) -ERROR(target_os_version_from_textual_interface_invalid,none, - "invalid target triple %0 in %1", - (StringRef, StringRef)) - ERROR(serialization_load_failed,Fatal, "failed to load module '%0'", (StringRef)) ERROR(module_interface_build_failed,Fatal, diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index 94515f6dbd890..61337e45c4404 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -123,6 +123,16 @@ namespace swift { llvm::VersionTuple getTargetSDKVersion(clang::DarwinSDKInfo &SDKInfo, const llvm::Triple &triple); + /// Compute a target triple that is canonicalized using the passed triple. + /// \returns nullopt if computation fails. + std::optional getCanonicalTriple(const llvm::Triple &triple); + + /// Compare triples for equality but also including OSVersion. + inline bool areTriplesStrictlyEqual(const llvm::Triple &lhs, + const llvm::Triple &rhs) { + return (lhs == rhs) && (lhs.getOSVersion() == rhs.getOSVersion()); + } + /// Get SDK build version. std::string getSDKBuildVersion(StringRef SDKPath); std::string getSDKBuildVersionFromPlist(StringRef Path); diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index 107159f9dab37..40959a1b95b25 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -848,6 +848,37 @@ llvm::VersionTuple swift::getTargetSDKVersion(clang::DarwinSDKInfo &SDKInfo, return SDKVersion; } +std::optional +swift::getCanonicalTriple(const llvm::Triple &triple) { + llvm::Triple Result = triple; + // Non-darwin targets do not require canonicalization. + if (!triple.isOSDarwin()) + return Result; + + // If the OS versions stay the same, return back the same triple. + const llvm::VersionTuple inputOSVersion = triple.getOSVersion(); + const bool isOSVersionInValidRange = + llvm::Triple::isValidVersionForOS(triple.getOS(), inputOSVersion); + const llvm::VersionTuple canonicalVersion = + llvm::Triple::getCanonicalVersionForOS( + triple.getOS(), triple.getOSVersion(), isOSVersionInValidRange); + if (canonicalVersion == triple.getOSVersion()) + return Result; + + const std::string inputOSName = triple.getOSName().str(); + const std::string inputOSVersionAsStr = inputOSVersion.getAsString(); + const int platformNameLength = + inputOSName.size() - inputOSVersionAsStr.size(); + if (!StringRef(inputOSName).ends_with(inputOSVersionAsStr) || + (platformNameLength <= 0)) + return std::nullopt; + + llvm::SmallString<64> buffer(inputOSName.substr(0, platformNameLength)); + buffer.append(canonicalVersion.getAsString()); + Result.setOSName(buffer.str()); + return Result; +} + static std::string getPlistEntry(const llvm::Twine &Path, StringRef KeyName) { auto BufOrErr = llvm::MemoryBuffer::getFile(Path); if (!BufOrErr) { diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 9db9c4ca8b89d..365b5e7aac00e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -478,6 +478,11 @@ void importer::getNormalInvocationArguments( if (LangOpts.ClangTarget.has_value() && !ignoreClangTarget) { triple = LangOpts.ClangTarget.value(); } + auto canonicalTriple = getCanonicalTriple(triple); + if (canonicalTriple.has_value() && + !areTriplesStrictlyEqual(*canonicalTriple, triple)) + triple = *canonicalTriple; + SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; ClangImporterOptions &importerOpts = ctx.ClangImporterOpts; auto languageVersion = ctx.LangOpts.EffectiveLanguageVersion; @@ -808,6 +813,11 @@ importer::addCommonInvocationArguments( if (ctx.LangOpts.ClangTarget.has_value() && !ignoreClangTarget) { triple = ctx.LangOpts.ClangTarget.value(); } + auto canonicalTriple = getCanonicalTriple(triple); + if (canonicalTriple.has_value() && + !areTriplesStrictlyEqual(*canonicalTriple, triple)) + triple = *canonicalTriple; + SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; const ClangImporterOptions &importerOpts = ctx.ClangImporterOpts; @@ -856,6 +866,12 @@ importer::addCommonInvocationArguments( invocationArgStrs.push_back("-darwin-target-variant"); if (ctx.LangOpts.ClangTargetVariant.has_value() && !ignoreClangTarget) variantTriple = ctx.LangOpts.ClangTargetVariant.value(); + + auto canonicalVariantTriple = getCanonicalTriple(*variantTriple); + if (canonicalVariantTriple.has_value() && + !areTriplesStrictlyEqual(*canonicalVariantTriple, *variantTriple)) + *variantTriple = *canonicalVariantTriple; + invocationArgStrs.push_back(variantTriple->str()); } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 5862adf180171..06e4339f5aba0 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1497,43 +1497,27 @@ bool swift::extractCompilerFlagsFromInterface( shouldModify = true; } - // Diagnose if the version in the target triple parsed from the - // swiftinterface is invalid for the OS. - const llvm::VersionTuple originalVer = triple.getOSVersion(); - bool isValidVersion = - llvm::Triple::isValidVersionForOS(triple.getOS(), originalVer); - if (!isValidVersion) { + // Canonicalize the version in the target triple parsed from the + // swiftinterface. + auto canonicalTriple = getCanonicalTriple(triple); + if (!canonicalTriple.has_value()) { if (Diag) { + const llvm::VersionTuple OSVersion = triple.getOSVersion(); + const bool isOSVersionInValidRange = + llvm::Triple::isValidVersionForOS(triple.getOS(), OSVersion); + const llvm::VersionTuple canonicalVersion = + llvm::Triple::getCanonicalVersionForOS( + triple.getOS(), triple.getOSVersion(), isOSVersionInValidRange); Diag->diagnose(SourceLoc(), - diag::target_os_version_from_textual_interface_invalid, - triple.str(), interfacePath); + diag::map_os_version_from_textual_interface_failed, + OSVersion.getAsString(), canonicalVersion.getAsString(), + interfacePath); } break; } - - // Canonicalize the version in the target triple parsed from the - // swiftinterface. - llvm::VersionTuple newVer = llvm::Triple::getCanonicalVersionForOS( - triple.getOS(), originalVer, isValidVersion); - if (originalVer != newVer) { - std::string originalOSName = triple.getOSName().str(); - std::string originalVerStr = originalVer.getAsString(); - std::string newVerStr = newVer.getAsString(); - const int OSNameWithoutVersionLength = - originalOSName.size() - originalVerStr.size(); - if (!StringRef(originalOSName).ends_with(originalVerStr) || - (OSNameWithoutVersionLength <= 0)) { - if (Diag) { - Diag->diagnose(SourceLoc(), - diag::map_os_version_from_textual_interface_failed, - originalVerStr, newVerStr, interfacePath); - } - break; - } - llvm::SmallString<64> buffer( - originalOSName.substr(0, OSNameWithoutVersionLength)); - buffer.append(newVerStr); - triple.setOSName(buffer.str()); + // Update the triple to use if it differs. + if (!areTriplesStrictlyEqual(triple, *canonicalTriple)) { + triple = *canonicalTriple; shouldModify = true; } if (shouldModify) diff --git a/test/ModuleInterface/canonicalized-os-version.swift b/test/ModuleInterface/canonicalized-os-version.swift new file mode 100644 index 0000000000000..f8137024b3478 --- /dev/null +++ b/test/ModuleInterface/canonicalized-os-version.swift @@ -0,0 +1,53 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) +// RUN: split-file %s %t + +// REQUIRES: OS=macosx || OS=maccatalyst + +// First, test that the swift interface with an invalid os version behaves fine. +// RUN: %target-swift-typecheck-module-from-interface(%t/Modules/Simple.swiftmodule/arm64-apple-macos.swiftinterface) -module-name Simple + +// Next, build transitive dependencies in zippered mode. +// RUN: %target-swift-frontend -module-name input %t/input.swift -target arm64-apple-macosx50.1 -target-variant arm64-apple-ios50.1-macabi -I%t/Modules -scan-dependencies -module-cache-path %t/module-cache-path -o %t/deps.json 2>&1 | Filecheck --allow-empty --implicit-check-not warning: --implicit-check-not error: %s +// RUN: %validate-json %t/deps.json | %FileCheck %s --check-prefix=DEPS + +DEPS-NOT: "arm64-apple-macos16.4" +DEPS-NOT: "arm64-apple-ios22.0-macabi" +DEPS: "arm64-apple-macos26.4" + +//--- Modules/Simple.swiftmodule/arm64-apple-macos.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -target arm64-apple-macos16.4 +public struct S { +} + +//--- Modules/Simple.swiftmodule/arm64-apple-ios-macabi.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -target arm64-apple-ios22.0-macabi +public struct S { +} + +//--- Modules/module.modulemap +module ClangDep { + header "ClangDep.h" + export * +} + +//--- Modules/ClangDep.h +typedef int my_int; + + +//--- Modules/Interopt.swiftmodule/arm64-apple-macos.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -target arm64-apple-macos16.4 +import Simple +import ClangDep + +//--- Modules/Interopt.swiftmodule/arm64-apple-ios-macabi.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -target arm64-apple-ios22.0-macabi +import Simple +import ClangDep + +//--- input.swift +import Interopt