Skip to content

Commit b43ed62

Browse files
authored
Merge pull request #65960 from tshortli/swiftinterface-inlinable-function-availability-miscompile-5.9
[5.9] Frontend: Don't append `-target-min-inlining-target target` to implicit module builds
2 parents cf6439d + 552fcb3 commit b43ed62

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)