Skip to content

Commit 0e7ad1e

Browse files
committed
Frontend: Don't append -target-min-inlining-target target to implicit module builds.
When performing an implicit module build, the frontend was prepending `-target-min-inlining-target target` to the command line. This was overriding the implicit `-target-min-inlining-target min` argument that is implied when `-library-level api` is specified. As a result, the wrong overload could be picked when compiling the body of an inlinable function to SIL for emission into the client, potentially resulting in crashes when the client of the module is back deployed to an older OS. Resolves rdar://109336472
1 parent 0594efc commit 0e7ad1e

7 files changed

+175
-43
lines changed

Diff for: include/swift/Frontend/ModuleInterfaceLoader.h

+13-2
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,18 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase {
532532
DependencyTracker *tracker = nullptr);
533533
};
534534

535+
struct SwiftInterfaceInfo {
536+
/// The compiler arguments that were encoded in the swiftinterface.
537+
SmallVector<const char *, 64> Arguments;
538+
539+
/// The string following `swift-compiler-version:` in the swiftinterface.
540+
std::string CompilerVersion;
541+
542+
/// The tools version of the compiler (e.g. 5.8) that emitted the
543+
/// swiftinterface. This is extracted from the `CompilerVersion` string.
544+
llvm::Optional<version::Version> CompilerToolsVersion;
545+
};
546+
535547
struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate {
536548
private:
537549
SourceManager &SM;
@@ -558,8 +570,7 @@ struct InterfaceSubContextDelegateImpl: InterfaceSubContextDelegate {
558570
bool suppressRemarks,
559571
RequireOSSAModules_t requireOSSAModules);
560572
bool extractSwiftInterfaceVersionAndArgs(CompilerInvocation &subInvocation,
561-
SmallVectorImpl<const char *> &SubArgs,
562-
std::string &CompilerVersion,
573+
SwiftInterfaceInfo &interfaceInfo,
563574
StringRef interfacePath,
564575
SourceLoc diagnosticLoc);
565576
public:

Diff for: include/swift/Frontend/ModuleInterfaceSupport.h

+18
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,27 @@ struct ModuleInterfaceOptions {
7575
extern version::Version InterfaceFormatVersion;
7676
std::string getSwiftInterfaceCompilerVersionForCurrentCompiler(ASTContext &ctx);
7777

78+
/// A regex that matches lines like this:
79+
///
80+
/// // swift-interface-format-version: 1.0
81+
///
82+
/// and extracts "1.0".
7883
llvm::Regex getSwiftInterfaceFormatVersionRegex();
84+
85+
/// A regex that matches lines like this:
86+
///
87+
/// // swift-compiler-version: Apple Swift version 5.8 (swiftlang-5.8.0.117.59)
88+
///
89+
/// and extracts "Apple Swift version 5.8 (swiftlang-5.8.0.117.59)".
7990
llvm::Regex getSwiftInterfaceCompilerVersionRegex();
8091

92+
/// A regex that matches strings like this:
93+
///
94+
/// Apple Swift version 5.8
95+
///
96+
/// and extracts "5.8".
97+
llvm::Regex getSwiftInterfaceCompilerToolsVersionRegex();
98+
8199
/// Emit a stable module interface for \p M, which can be used by a client
82100
/// source file to import this module, subject to options given by \p Opts.
83101
///

Diff for: lib/Frontend/ModuleInterfaceLoader.cpp

+49-41
Original file line numberDiff line numberDiff line change
@@ -1330,10 +1330,12 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface(
13301330
SearchPathOpts.CandidateCompiledModules);
13311331
}
13321332

1333-
static bool readSwiftInterfaceVersionAndArgs(
1334-
SourceManager &SM, DiagnosticEngine &Diags, llvm::StringSaver &ArgSaver,
1335-
SmallVectorImpl<const char *> &SubArgs, std::string &CompilerVersion,
1336-
StringRef interfacePath, SourceLoc diagnosticLoc) {
1333+
static bool readSwiftInterfaceVersionAndArgs(SourceManager &SM,
1334+
DiagnosticEngine &Diags,
1335+
llvm::StringSaver &ArgSaver,
1336+
SwiftInterfaceInfo &interfaceInfo,
1337+
StringRef interfacePath,
1338+
SourceLoc diagnosticLoc) {
13371339
llvm::vfs::FileSystem &fs = *SM.getFileSystem();
13381340
auto FileOrError = swift::vfs::getFileOrSTDIN(fs, interfacePath);
13391341
if (!FileOrError) {
@@ -1346,7 +1348,7 @@ static bool readSwiftInterfaceVersionAndArgs(
13461348
auto SB = FileOrError.get()->getBuffer();
13471349
auto VersRe = getSwiftInterfaceFormatVersionRegex();
13481350
auto CompRe = getSwiftInterfaceCompilerVersionRegex();
1349-
SmallVector<StringRef, 1> VersMatches, CompMatches;
1351+
SmallVector<StringRef, 2> VersMatches, CompMatches;
13501352

13511353
if (!VersRe.match(SB, &VersMatches)) {
13521354
InterfaceSubContextDelegateImpl::diagnose(
@@ -1355,7 +1357,8 @@ static bool readSwiftInterfaceVersionAndArgs(
13551357
return true;
13561358
}
13571359

1358-
if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver, SubArgs)) {
1360+
if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver,
1361+
interfaceInfo.Arguments)) {
13591362
InterfaceSubContextDelegateImpl::diagnose(
13601363
interfacePath, diagnosticLoc, SM, &Diags,
13611364
diag::error_extracting_version_from_module_interface);
@@ -1375,10 +1378,20 @@ static bool readSwiftInterfaceVersionAndArgs(
13751378

13761379
if (CompRe.match(SB, &CompMatches)) {
13771380
assert(CompMatches.size() == 2);
1378-
CompilerVersion = ArgSaver.save(CompMatches[1]).str();
1381+
interfaceInfo.CompilerVersion = ArgSaver.save(CompMatches[1]).str();
1382+
1383+
// For now, successfully parsing the tools version out of the interface is
1384+
// optional.
1385+
auto ToolsVersRe = getSwiftInterfaceCompilerToolsVersionRegex();
1386+
SmallVector<StringRef, 2> VendorToolsVersMatches;
1387+
if (ToolsVersRe.match(interfaceInfo.CompilerVersion,
1388+
&VendorToolsVersMatches)) {
1389+
interfaceInfo.CompilerToolsVersion = VersionParser::parseVersionString(
1390+
VendorToolsVersMatches[1], SourceLoc(), nullptr);
1391+
}
13791392
} else {
13801393
// Don't diagnose; handwritten module interfaces don't include this field.
1381-
CompilerVersion = "(unspecified, file possibly handwritten)";
1394+
interfaceInfo.CompilerVersion = "(unspecified, file possibly handwritten)";
13821395
}
13831396

13841397
// For now: we support anything with the same "major version" and assume
@@ -1425,23 +1438,18 @@ bool ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
14251438
// Read out the compiler version.
14261439
llvm::BumpPtrAllocator alloc;
14271440
llvm::StringSaver ArgSaver(alloc);
1428-
std::string CompilerVersion;
1429-
SmallVector<const char *, 64> InterfaceArgs;
1430-
readSwiftInterfaceVersionAndArgs(Instance.getSourceMgr(),
1431-
Instance.getDiags(),
1432-
ArgSaver,
1433-
InterfaceArgs,
1434-
CompilerVersion,
1435-
interfacePath,
1441+
SwiftInterfaceInfo InterfaceInfo;
1442+
readSwiftInterfaceVersionAndArgs(Instance.getSourceMgr(), Instance.getDiags(),
1443+
ArgSaver, InterfaceInfo, interfacePath,
14361444
SourceLoc());
1437-
1445+
14381446
auto Builder = ExplicitModuleInterfaceBuilder(
14391447
Instance, &Instance.getDiags(), Instance.getSourceMgr(),
14401448
moduleCachePath, backupInterfaceDir, prebuiltCachePath,
14411449
ABIDescriptorPath, {});
14421450
auto error = Builder.buildSwiftModuleFromInterface(
14431451
interfacePath, outputPath, ShouldSerializeDeps, /*ModuleBuffer*/nullptr,
1444-
CompiledCandidates, CompilerVersion);
1452+
CompiledCandidates, InterfaceInfo.CompilerVersion);
14451453
if (!error)
14461454
return false;
14471455
else
@@ -1567,18 +1575,14 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
15671575
}
15681576

15691577
bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs(
1570-
CompilerInvocation &subInvocation,
1571-
SmallVectorImpl<const char *> &SubArgs,
1572-
std::string &CompilerVersion,
1573-
StringRef interfacePath,
1574-
SourceLoc diagnosticLoc) {
1575-
if (readSwiftInterfaceVersionAndArgs(SM, *Diags, ArgSaver, SubArgs,
1576-
CompilerVersion, interfacePath,
1577-
diagnosticLoc))
1578+
CompilerInvocation &subInvocation, SwiftInterfaceInfo &interfaceInfo,
1579+
StringRef interfacePath, SourceLoc diagnosticLoc) {
1580+
if (readSwiftInterfaceVersionAndArgs(SM, *Diags, ArgSaver, interfaceInfo,
1581+
interfacePath, diagnosticLoc))
15781582
return true;
15791583

15801584
SmallString<32> ExpectedModuleName = subInvocation.getModuleName();
1581-
if (subInvocation.parseArgs(SubArgs, *Diags)) {
1585+
if (subInvocation.parseArgs(interfaceInfo.Arguments, *Diags)) {
15821586
return true;
15831587
}
15841588

@@ -1845,24 +1849,28 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName,
18451849
.setMainAndSupplementaryOutputs(outputFiles, ModuleOutputPaths);
18461850

18471851
SmallVector<const char *, 64> SubArgs;
1848-
1849-
// If the interface was emitted by a compiler that didn't print
1850-
// `-target-min-inlining-version` into it, default to using the version from
1851-
// the target triple, emulating previous behavior.
1852-
SubArgs.push_back("-target-min-inlining-version");
1853-
SubArgs.push_back("target");
1854-
1855-
std::string CompilerVersion;
1852+
SwiftInterfaceInfo interfaceInfo;
18561853
// Extract compiler arguments from the interface file and use them to configure
18571854
// the compiler invocation.
1858-
if (extractSwiftInterfaceVersionAndArgs(subInvocation,
1859-
SubArgs,
1860-
CompilerVersion,
1861-
interfacePath,
1862-
diagLoc)) {
1855+
if (extractSwiftInterfaceVersionAndArgs(subInvocation, interfaceInfo,
1856+
interfacePath, diagLoc)) {
18631857
return std::make_error_code(std::errc::not_supported);
18641858
}
18651859

1860+
// Prior to Swift 5.9, swiftinterfaces were always built (accidentally) with
1861+
// `-target-min-inlining-version target` prepended to the argument list. To
1862+
// preserve compatibility we must continue to prepend those flags to the
1863+
// invocation when the interface was generated by an older compiler.
1864+
if (auto toolsVersion = interfaceInfo.CompilerToolsVersion) {
1865+
if (toolsVersion < version::Version{5, 9}) {
1866+
SubArgs.push_back("-target-min-inlining-version");
1867+
SubArgs.push_back("target");
1868+
}
1869+
}
1870+
1871+
SubArgs.insert(SubArgs.end(), interfaceInfo.Arguments.begin(),
1872+
interfaceInfo.Arguments.end());
1873+
18661874
// Insert arguments collected from the interface file.
18671875
BuildArgs.insert(BuildArgs.end(), SubArgs.begin(), SubArgs.end());
18681876
if (subInvocation.parseArgs(SubArgs, *Diags)) {
@@ -1891,7 +1899,7 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName,
18911899
CompilerInstance subInstance;
18921900
SubCompilerInstanceInfo info;
18931901
info.Instance = &subInstance;
1894-
info.CompilerVersion = CompilerVersion;
1902+
info.CompilerVersion = interfaceInfo.CompilerVersion;
18951903

18961904
subInstance.getSourceMgr().setFileSystem(SM.getFileSystem());
18971905

Diff for: lib/Frontend/ModuleInterfaceSupport.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ llvm::Regex swift::getSwiftInterfaceCompilerVersionRegex() {
122122
": (.+)$", llvm::Regex::Newline);
123123
}
124124

125+
llvm::Regex swift::getSwiftInterfaceCompilerToolsVersionRegex() {
126+
return llvm::Regex("Swift version ([0-9\\.]+)", llvm::Regex::Newline);
127+
}
128+
125129
// MARK(https://github.com/apple/swift/issues/43510): Module name shadowing warnings
126130
//
127131
// When swiftc emits a module interface, it qualifies most types with their
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/NonAPI)
3+
// RUN: %empty-directory(%t/API)
4+
// RUN: split-file %s %t
5+
6+
// RUN: %target-swift-emit-module-interface(%t/NonAPI/Library.swiftinterface) %t/Library.swift -module-name Library -target %target-swift-abi-5.8-triple
7+
// RUN: %target-swift-emit-module-interface(%t/API/Library.swiftinterface) %t/Library.swift -module-name Library -target %target-swift-abi-5.8-triple -library-level api
8+
9+
// Build Client.swift against the Library.swiftinterface without
10+
// `-library-level api`. Since the deployment target of the library is
11+
// SwiftStdlib 5.8, the newer overload that returns a String should be selected
12+
// by overload resolution during the implicit module build.
13+
14+
// RUN: %target-build-swift %t/Client.swift -o %t/NonAPI/client -I %t/NonAPI/
15+
// RUN: %target-codesign %t/NonAPI/client
16+
// RUN: %target-run %t/NonAPI/client | %FileCheck %s --check-prefix=CHECK-NON-API
17+
18+
// Build Client.swift against the Library.swiftinterface with
19+
// `-library-level api`. Since the deployment target of the client that will
20+
// get a copy of `fragileFuncUsingOverload()` is earlier than SwiftStdlib 5.8,
21+
// the older overload returning an Int should be selected during the implicit
22+
// module build even though the library targets SwiftStdlib 5.8.
23+
24+
// RUN: %target-build-swift %t/Client.swift -o %t/API/client -I %t/API/
25+
// RUN: %target-codesign %t/API/client
26+
// RUN: %target-run %t/API/client | %FileCheck %s --check-prefix=CHECK-API
27+
28+
// REQUIRES: executable_test
29+
// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos
30+
31+
//--- Library.swift
32+
33+
@_disfavoredOverload
34+
@_alwaysEmitIntoClient
35+
public func overloadedFunc() -> Int {
36+
return 1234
37+
}
38+
39+
@available(SwiftStdlib 5.8, *)
40+
@_alwaysEmitIntoClient
41+
public func overloadedFunc() -> String {
42+
return "String"
43+
}
44+
45+
@_alwaysEmitIntoClient
46+
public func fragileFuncUsingOverload() -> any CustomStringConvertible {
47+
return overloadedFunc()
48+
}
49+
50+
//--- Client.swift
51+
52+
import Library
53+
54+
// CHECK-NON-API: String
55+
// CHECK-API: 1234
56+
print(fragileFuncUsingOverload())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-compiler-version: Apple Swift version 5.8 (swiftlang-5.8.0.117.59 clang-1403.0.22.8.50)
3+
// swift-module-flags: -target arm64-apple-macosx11 -enable-library-evolution -swift-version 5 -library-level api -module-name Test
4+
5+
// RUN: %target-swift-frontend -typecheck-module-from-interface -verify -module-name Test %s
6+
7+
// REQUIRES: OS=macosx
8+
9+
import Swift
10+
11+
@available(macOS 11, *)
12+
public struct S {}
13+
14+
// This typealias ought to be @available(macOS 11, *) since it references `S`
15+
// and the module was compiled with `-library-level api`. However, given that
16+
// the interface was produced with tools that are older than Swift 5.9 we
17+
// typecheck availability with the deployment target as the floor.
18+
public typealias A = S
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-compiler-version: Apple Swift version 5.9
3+
// swift-module-flags: -target arm64-apple-macosx11 -enable-library-evolution -swift-version 5 -library-level api -module-name Test
4+
5+
// RUN: not %target-swift-frontend -typecheck-module-from-interface -module-name Test %s 2>&1 | %FileCheck %s
6+
7+
// REQUIRES: OS=macosx
8+
9+
import Swift
10+
11+
@available(macOS 11, *)
12+
public struct S {}
13+
14+
public typealias A = S
15+
16+
// CHECK: error: 'S' is only available in macOS 11 or newer; clients of 'Test' may have a lower deployment target
17+
// CHECK: error: failed to verify module interface of 'Test' due to the errors above;

0 commit comments

Comments
 (0)