Skip to content

Commit 552fcb3

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 841c410 commit 552fcb3

7 files changed

+175
-43
lines changed

include/swift/Frontend/ModuleInterfaceLoader.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,18 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase {
531531
DependencyTracker *tracker = nullptr);
532532
};
533533

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

include/swift/Frontend/ModuleInterfaceSupport.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,27 @@ struct ModuleInterfaceOptions {
7070
extern version::Version InterfaceFormatVersion;
7171
std::string getSwiftInterfaceCompilerVersionForCurrentCompiler(ASTContext &ctx);
7272

73+
/// A regex that matches lines like this:
74+
///
75+
/// // swift-interface-format-version: 1.0
76+
///
77+
/// and extracts "1.0".
7378
llvm::Regex getSwiftInterfaceFormatVersionRegex();
79+
80+
/// A regex that matches lines like this:
81+
///
82+
/// // swift-compiler-version: Apple Swift version 5.8 (swiftlang-5.8.0.117.59)
83+
///
84+
/// and extracts "Apple Swift version 5.8 (swiftlang-5.8.0.117.59)".
7485
llvm::Regex getSwiftInterfaceCompilerVersionRegex();
7586

87+
/// A regex that matches strings like this:
88+
///
89+
/// Apple Swift version 5.8
90+
///
91+
/// and extracts "5.8".
92+
llvm::Regex getSwiftInterfaceCompilerToolsVersionRegex();
93+
7694
/// Emit a stable module interface for \p M, which can be used by a client
7795
/// source file to import this module, subject to options given by \p Opts.
7896
///

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,10 +1332,12 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface(
13321332
SearchPathOpts.CandidateCompiledModules);
13331333
}
13341334

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

13531355
if (!VersRe.match(SB, &VersMatches)) {
13541356
InterfaceSubContextDelegateImpl::diagnose(
@@ -1357,7 +1359,8 @@ static bool readSwiftInterfaceVersionAndArgs(
13571359
return true;
13581360
}
13591361

1360-
if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver, SubArgs)) {
1362+
if (extractCompilerFlagsFromInterface(interfacePath, SB, ArgSaver,
1363+
interfaceInfo.Arguments)) {
13611364
InterfaceSubContextDelegateImpl::diagnose(
13621365
interfacePath, diagnosticLoc, SM, &Diags,
13631366
diag::error_extracting_version_from_module_interface);
@@ -1377,10 +1380,20 @@ static bool readSwiftInterfaceVersionAndArgs(
13771380

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

13861399
// For now: we support anything with the same "major version" and assume
@@ -1421,23 +1434,18 @@ bool ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
14211434
// Read out the compiler version.
14221435
llvm::BumpPtrAllocator alloc;
14231436
llvm::StringSaver ArgSaver(alloc);
1424-
std::string CompilerVersion;
1425-
SmallVector<const char *, 64> InterfaceArgs;
1426-
readSwiftInterfaceVersionAndArgs(Instance.getSourceMgr(),
1427-
Instance.getDiags(),
1428-
ArgSaver,
1429-
InterfaceArgs,
1430-
CompilerVersion,
1431-
interfacePath,
1437+
SwiftInterfaceInfo InterfaceInfo;
1438+
readSwiftInterfaceVersionAndArgs(Instance.getSourceMgr(), Instance.getDiags(),
1439+
ArgSaver, InterfaceInfo, interfacePath,
14321440
SourceLoc());
1433-
1441+
14341442
auto Builder = ExplicitModuleInterfaceBuilder(
14351443
Instance, &Instance.getDiags(), Instance.getSourceMgr(),
14361444
moduleCachePath, backupInterfaceDir, prebuiltCachePath,
14371445
ABIDescriptorPath, {});
14381446
auto error = Builder.buildSwiftModuleFromInterface(
14391447
interfacePath, outputPath, ShouldSerializeDeps, /*ModuleBuffer*/nullptr,
1440-
CompiledCandidates, CompilerVersion);
1448+
CompiledCandidates, InterfaceInfo.CompilerVersion);
14411449
if (!error)
14421450
return false;
14431451
else
@@ -1563,18 +1571,14 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
15631571
}
15641572

15651573
bool InterfaceSubContextDelegateImpl::extractSwiftInterfaceVersionAndArgs(
1566-
CompilerInvocation &subInvocation,
1567-
SmallVectorImpl<const char *> &SubArgs,
1568-
std::string &CompilerVersion,
1569-
StringRef interfacePath,
1570-
SourceLoc diagnosticLoc) {
1571-
if (readSwiftInterfaceVersionAndArgs(SM, *Diags, ArgSaver, SubArgs,
1572-
CompilerVersion, interfacePath,
1573-
diagnosticLoc))
1574+
CompilerInvocation &subInvocation, SwiftInterfaceInfo &interfaceInfo,
1575+
StringRef interfacePath, SourceLoc diagnosticLoc) {
1576+
if (readSwiftInterfaceVersionAndArgs(SM, *Diags, ArgSaver, interfaceInfo,
1577+
interfacePath, diagnosticLoc))
15741578
return true;
15751579

15761580
SmallString<32> ExpectedModuleName = subInvocation.getModuleName();
1577-
if (subInvocation.parseArgs(SubArgs, *Diags)) {
1581+
if (subInvocation.parseArgs(interfaceInfo.Arguments, *Diags)) {
15781582
return true;
15791583
}
15801584

@@ -1834,24 +1838,28 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName,
18341838
.setMainAndSupplementaryOutputs(outputFiles, ModuleOutputPaths);
18351839

18361840
SmallVector<const char *, 64> SubArgs;
1837-
1838-
// If the interface was emitted by a compiler that didn't print
1839-
// `-target-min-inlining-version` into it, default to using the version from
1840-
// the target triple, emulating previous behavior.
1841-
SubArgs.push_back("-target-min-inlining-version");
1842-
SubArgs.push_back("target");
1843-
1844-
std::string CompilerVersion;
1841+
SwiftInterfaceInfo interfaceInfo;
18451842
// Extract compiler arguments from the interface file and use them to configure
18461843
// the compiler invocation.
1847-
if (extractSwiftInterfaceVersionAndArgs(subInvocation,
1848-
SubArgs,
1849-
CompilerVersion,
1850-
interfacePath,
1851-
diagLoc)) {
1844+
if (extractSwiftInterfaceVersionAndArgs(subInvocation, interfaceInfo,
1845+
interfacePath, diagLoc)) {
18521846
return std::make_error_code(std::errc::not_supported);
18531847
}
18541848

1849+
// Prior to Swift 5.9, swiftinterfaces were always built (accidentally) with
1850+
// `-target-min-inlining-version target` prepended to the argument list. To
1851+
// preserve compatibility we must continue to prepend those flags to the
1852+
// invocation when the interface was generated by an older compiler.
1853+
if (auto toolsVersion = interfaceInfo.CompilerToolsVersion) {
1854+
if (toolsVersion < version::Version{5, 9}) {
1855+
SubArgs.push_back("-target-min-inlining-version");
1856+
SubArgs.push_back("target");
1857+
}
1858+
}
1859+
1860+
SubArgs.insert(SubArgs.end(), interfaceInfo.Arguments.begin(),
1861+
interfaceInfo.Arguments.end());
1862+
18551863
// Insert arguments collected from the interface file.
18561864
BuildArgs.insert(BuildArgs.end(), SubArgs.begin(), SubArgs.end());
18571865
if (subInvocation.parseArgs(SubArgs, *Diags)) {
@@ -1880,7 +1888,7 @@ InterfaceSubContextDelegateImpl::runInSubCompilerInstance(StringRef moduleName,
18801888
CompilerInstance subInstance;
18811889
SubCompilerInstanceInfo info;
18821890
info.Instance = &subInstance;
1883-
info.CompilerVersion = CompilerVersion;
1891+
info.CompilerVersion = interfaceInfo.CompilerVersion;
18841892

18851893
subInstance.getSourceMgr().setFileSystem(SM.getFileSystem());
18861894

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ llvm::Regex swift::getSwiftInterfaceCompilerVersionRegex() {
116116
": (.+)$", llvm::Regex::Newline);
117117
}
118118

119+
llvm::Regex swift::getSwiftInterfaceCompilerToolsVersionRegex() {
120+
return llvm::Regex("Swift version ([0-9\\.]+)", llvm::Regex::Newline);
121+
}
122+
119123
// MARK(https://github.com/apple/swift/issues/43510): Module name shadowing warnings
120124
//
121125
// When swiftc emits a module interface, it qualifies most types with their
Lines changed: 56 additions & 0 deletions
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())
Lines changed: 18 additions & 0 deletions
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
Lines changed: 17 additions & 0 deletions
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)