Skip to content

Commit 17d121f

Browse files
authored
Merge pull request swiftlang#65954 from tshortli/swiftinterface-inlinable-function-availability-miscompile
Frontend: Don't append `-target-min-inlining-target target` to implicit module builds
2 parents bde0426 + 0e7ad1e commit 17d121f

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

include/swift/Frontend/ModuleInterfaceSupport.h

Lines changed: 18 additions & 0 deletions
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
///

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 49 additions & 41 deletions
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

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 4 additions & 0 deletions
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
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)