Skip to content

Commit f45095e

Browse files
[ExplicitModule] Allow typecheck-module-from-interface using explicit module
Allow `-typecheck-module-from-interface` using explicit module instead of building implicit module. This setups swift-frontend to accept explicit module build arguments and loading explicit module during verifying. SwiftDriver needs to setup correct arguments including the output path for swift module to fully enable explicit module interface check.
1 parent 390beb9 commit f45095e

10 files changed

+154
-60
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ WARNING(warning_cannot_find_locale_file,none,
105105
WARNING(warning_cannot_multithread_batch_mode,none,
106106
"ignoring -num-threads argument; cannot multithread batch mode", ())
107107
ERROR(error_cannot_explicit_interface_build_in_mode,none,
108-
"'-explicit-interface-module-build' only supported when building a module from interface ('-compile-module-from-interface')'", ())
108+
"'-explicit-interface-module-build' only supported when building a module from interface ('-compile-module-from-interface' or '-typecheck-module-from-interface')'", ())
109109
ERROR(error_cannot_direct_cc1_pcm_build_in_mode,none,
110110
"'-direct-clang-cc1-module-build' only supported when building a PCM ('-emit-pcm')'", ())
111111
ERROR(error_unsupported_option_argument,none,

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ class FrontendOptions {
141141
/// Clang Include Trees.
142142
std::vector<std::string> ClangIncludeTrees;
143143

144+
/// CacheKey for input file.
145+
std::string InputFileKey;
146+
144147
/// Number of retry opening an input file if the previous opening returns
145148
/// bad file descriptor error.
146149
unsigned BadFileDescriptorRetryCount = 0;

include/swift/Option/FrontendOptions.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,8 @@ def always_compile_output_files :
12251225
def allow_unstable_cache_key_for_testing: Flag<["-"], "allow-unstable-cache-key-for-testing">,
12261226
HelpText<"Allow compilation caching with unstable inputs for testing purpose">;
12271227

1228+
def input_file_key : Separate<["-"], "input-file-key">,
1229+
HelpText<"Cache Key for input file">;
12281230
def bridging_header_pch_key : Separate<["-"], "bridging-header-pch-key">,
12291231
HelpText<"Cache Key for bridging header pch">;
12301232

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,33 @@ bool ArgsToFrontendOptionsConverter::convert(
249249
if (checkBuildFromInterfaceOnlyOptions())
250250
return true;
251251

252+
Opts.DeterministicCheck = Args.hasArg(OPT_enable_deterministic_check);
253+
Opts.EnableCaching = Args.hasArg(OPT_cache_compile_job);
254+
Opts.EnableCachingRemarks = Args.hasArg(OPT_cache_remarks);
255+
Opts.CacheSkipReplay = Args.hasArg(OPT_cache_disable_replay);
256+
Opts.CASOpts.CASPath =
257+
Args.getLastArgValue(OPT_cas_path, llvm::cas::getDefaultOnDiskCASPath());
258+
Opts.CASOpts.PluginPath = Args.getLastArgValue(OPT_cas_plugin_path);
259+
for (StringRef Opt : Args.getAllArgValues(OPT_cas_plugin_option)) {
260+
StringRef Name, Value;
261+
std::tie(Name, Value) = Opt.split('=');
262+
Opts.CASOpts.PluginOptions.emplace_back(std::string(Name),
263+
std::string(Value));
264+
}
265+
266+
Opts.CASFSRootIDs = Args.getAllArgValues(OPT_cas_fs);
267+
Opts.ClangIncludeTrees = Args.getAllArgValues(OPT_clang_include_tree_root);
268+
Opts.InputFileKey = Args.getLastArgValue(OPT_input_file_key);
269+
270+
if (Opts.EnableCaching && Opts.CASFSRootIDs.empty() &&
271+
Opts.ClangIncludeTrees.empty() &&
272+
FrontendOptions::supportCompilationCaching(Opts.RequestedAction)) {
273+
if (!Args.hasArg(OPT_allow_unstable_cache_key_for_testing)) {
274+
Diags.diagnose(SourceLoc(), diag::error_caching_no_cas_fs);
275+
return true;
276+
}
277+
}
278+
252279
if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction)) {
253280
if (Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies) ||
254281
Args.hasArg(OPT_experimental_skip_all_function_bodies) ||
@@ -346,36 +373,10 @@ bool ArgsToFrontendOptionsConverter::convert(
346373
Opts.serializedPathObfuscator.addMapping(SplitMap.first, SplitMap.second);
347374
}
348375
Opts.emptyABIDescriptor = Args.hasArg(OPT_empty_abi_descriptor);
349-
Opts.DeterministicCheck = Args.hasArg(OPT_enable_deterministic_check);
350376
for (auto A : Args.getAllArgValues(options::OPT_block_list_file)) {
351377
Opts.BlocklistConfigFilePaths.push_back(A);
352378
}
353379

354-
Opts.EnableCaching = Args.hasArg(OPT_cache_compile_job);
355-
Opts.EnableCachingRemarks = Args.hasArg(OPT_cache_remarks);
356-
Opts.CacheSkipReplay = Args.hasArg(OPT_cache_disable_replay);
357-
Opts.CASOpts.CASPath =
358-
Args.getLastArgValue(OPT_cas_path, llvm::cas::getDefaultOnDiskCASPath());
359-
Opts.CASOpts.PluginPath = Args.getLastArgValue(OPT_cas_plugin_path);
360-
for (StringRef Opt : Args.getAllArgValues(OPT_cas_plugin_option)) {
361-
StringRef Name, Value;
362-
std::tie(Name, Value) = Opt.split('=');
363-
Opts.CASOpts.PluginOptions.emplace_back(std::string(Name),
364-
std::string(Value));
365-
}
366-
367-
Opts.CASFSRootIDs = Args.getAllArgValues(OPT_cas_fs);
368-
Opts.ClangIncludeTrees = Args.getAllArgValues(OPT_clang_include_tree_root);
369-
370-
if (Opts.EnableCaching && Opts.CASFSRootIDs.empty() &&
371-
Opts.ClangIncludeTrees.empty() &&
372-
FrontendOptions::supportCompilationCaching(Opts.RequestedAction)) {
373-
if (!Args.hasArg(OPT_allow_unstable_cache_key_for_testing)) {
374-
Diags.diagnose(SourceLoc(), diag::error_caching_no_cas_fs);
375-
return true;
376-
}
377-
}
378-
379380
return false;
380381
}
381382

@@ -706,7 +707,10 @@ bool ArgsToFrontendOptionsConverter::
706707

707708
bool ArgsToFrontendOptionsConverter::checkBuildFromInterfaceOnlyOptions()
708709
const {
709-
if (Opts.RequestedAction != FrontendOptions::ActionType::CompileModuleFromInterface &&
710+
if (Opts.RequestedAction !=
711+
FrontendOptions::ActionType::CompileModuleFromInterface &&
712+
Opts.RequestedAction !=
713+
FrontendOptions::ActionType::TypecheckModuleFromInterface &&
710714
Opts.ExplicitInterfaceBuild) {
711715
Diags.diagnose(SourceLoc(),
712716
diag::error_cannot_explicit_interface_build_in_mode);

lib/Frontend/Frontend.cpp

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -520,38 +520,44 @@ bool CompilerInstance::setup(const CompilerInvocation &Invoke,
520520
}
521521

522522
bool CompilerInstance::setUpVirtualFileSystemOverlays() {
523-
if (Invocation.getFrontendOptions().EnableCaching &&
524-
(!Invocation.getFrontendOptions().CASFSRootIDs.empty() ||
525-
!Invocation.getFrontendOptions().ClangIncludeTrees.empty())) {
526-
// Set up CASFS as BaseFS.
523+
if (Invocation.getFrontendOptions().EnableCaching) {
527524
const auto &Opts = getInvocation().getFrontendOptions();
528-
auto FS =
529-
createCASFileSystem(*CAS, Opts.CASFSRootIDs, Opts.ClangIncludeTrees);
530-
if (!FS) {
531-
Diagnostics.diagnose(SourceLoc(), diag::error_cas,
532-
toString(FS.takeError()));
533-
return true;
525+
if (!Invocation.getFrontendOptions().CASFSRootIDs.empty() ||
526+
!Invocation.getFrontendOptions().ClangIncludeTrees.empty()) {
527+
// Set up CASFS as BaseFS.
528+
auto FS =
529+
createCASFileSystem(*CAS, Opts.CASFSRootIDs, Opts.ClangIncludeTrees);
530+
if (!FS) {
531+
Diagnostics.diagnose(SourceLoc(), diag::error_cas,
532+
toString(FS.takeError()));
533+
return true;
534+
}
535+
SourceMgr.setFileSystem(std::move(*FS));
536+
}
537+
538+
// If we need to load any files from CAS, try load it now and overlay it.
539+
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> MemFS =
540+
new llvm::vfs::InMemoryFileSystem();
541+
const auto &ClangOpts = getInvocation().getClangImporterOptions();
542+
543+
if (!ClangOpts.BridgingHeaderPCHCacheKey.empty()) {
544+
if (auto loadedBuffer = loadCachedCompileResultFromCacheKey(
545+
getObjectStore(), getActionCache(), Diagnostics,
546+
ClangOpts.BridgingHeaderPCHCacheKey, ClangOpts.BridgingHeader))
547+
MemFS->addFile(Invocation.getClangImporterOptions().BridgingHeader, 0,
548+
std::move(loadedBuffer));
534549
}
535-
SourceMgr.setFileSystem(std::move(*FS));
536-
}
537-
538-
// If we have a bridging header cache key, try load it now and overlay it.
539-
if (!Invocation.getClangImporterOptions().BridgingHeaderPCHCacheKey.empty() &&
540-
Invocation.getFrontendOptions().EnableCaching) {
541-
auto loadedBridgingBuffer = loadCachedCompileResultFromCacheKey(
542-
getObjectStore(), getActionCache(), Diagnostics,
543-
Invocation.getClangImporterOptions().BridgingHeaderPCHCacheKey,
544-
Invocation.getClangImporterOptions().BridgingHeader);
545-
if (loadedBridgingBuffer) {
546-
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> PCHFS =
547-
new llvm::vfs::InMemoryFileSystem();
548-
PCHFS->addFile(Invocation.getClangImporterOptions().BridgingHeader, 0,
549-
std::move(loadedBridgingBuffer));
550-
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayVFS =
551-
new llvm::vfs::OverlayFileSystem(SourceMgr.getFileSystem());
552-
OverlayVFS->pushOverlay(PCHFS);
553-
SourceMgr.setFileSystem(std::move(OverlayVFS));
550+
if (!Opts.InputFileKey.empty()) {
551+
auto InputPath = Opts.InputsAndOutputs.getFilenameOfFirstInput();
552+
if (auto loadedBuffer = loadCachedCompileResultFromCacheKey(
553+
getObjectStore(), getActionCache(), Diagnostics,
554+
Opts.InputFileKey, InputPath))
555+
MemFS->addFile(InputPath, 0, std::move(loadedBuffer));
554556
}
557+
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayVFS =
558+
new llvm::vfs::OverlayFileSystem(SourceMgr.getFileSystem());
559+
OverlayVFS->pushOverlay(MemFS);
560+
SourceMgr.setFileSystem(std::move(OverlayVFS));
555561
}
556562

557563
auto ExpectedOverlay =

lib/Frontend/FrontendOptions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,6 @@ bool FrontendOptions::supportCompilationCaching(ActionType action) {
229229
case ActionType::DumpInterfaceHash:
230230
case ActionType::EmitImportedModules:
231231
case ActionType::ScanDependencies:
232-
case ActionType::TypecheckModuleFromInterface:
233232
case ActionType::ResolveImports:
234233
case ActionType::Typecheck:
235234
case ActionType::DumpAST:
@@ -241,6 +240,7 @@ bool FrontendOptions::supportCompilationCaching(ActionType action) {
241240
case ActionType::Immediate:
242241
case ActionType::DumpTypeInfo:
243242
return false;
243+
case ActionType::TypecheckModuleFromInterface:
244244
case ActionType::CompileModuleFromInterface:
245245
case ActionType::EmitPCH:
246246
case ActionType::EmitPCM:

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@ bool ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
14411441
StringRef outputPath, bool ShouldSerializeDeps,
14421442
ArrayRef<std::string> CompiledCandidates,
14431443
DependencyTracker *tracker) {
1444-
1444+
14451445
if (!Instance.getInvocation().getIRGenOptions().AlwaysCompile) {
14461446
// First, check if the expected output already exists and possibly
14471447
// up-to-date w.r.t. all of the dependencies it was built with. If so, early
@@ -1462,7 +1462,7 @@ bool ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
14621462
return false;
14631463
}
14641464
}
1465-
1465+
14661466
// Read out the compiler version.
14671467
llvm::BumpPtrAllocator alloc;
14681468
llvm::StringSaver ArgSaver(alloc);

test/CAS/Inputs/ExtractOutputKey.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Usage: ExtractOutputKey.py file.json OutputPath
4+
5+
import json
6+
import sys
7+
8+
input_json = sys.argv[1]
9+
output_path = sys.argv[2]
10+
11+
12+
with open(input_json, 'r') as file:
13+
outputs = json.load(file)
14+
for output in outputs:
15+
if output['OutputPath'] != output_path:
16+
continue
17+
print(output['CacheKey'])

test/CAS/cas-explicit-module-map.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,33 @@
110110

111111
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -disable-implicit-swift-modules -module-cache-path %t.module-cache -explicit-swift-module-map-file @%t/map.casid -Rmodule-loading -Xcc -Rmodule-import %s -cache-compile-job -cas-path %t/cas -allow-unstable-cache-key-for-testing 2>&1 | %FileCheck %s
112112

113+
// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t/Foo.swiftinterface -disable-implicit-swift-modules -module-cache-path %t.module-cache -explicit-swift-module-map-file @%t/map.casid %s -cache-compile-job -cas-path %t/cas -allow-unstable-cache-key-for-testing -swift-version 5 -enable-library-evolution
114+
// RUN: %cache-tool -cas-path %t/cas -cache-tool-action print-output-keys -- \
115+
// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t/Foo.swiftinterface -disable-implicit-swift-modules \
116+
// RUN: -module-cache-path %t.module-cache -explicit-swift-module-map-file @%t/map.casid %s -cache-compile-job \
117+
// RUN: -cas-path %t/cas -allow-unstable-cache-key-for-testing -swift-version 5 -enable-library-evolution > %t/keys.json
118+
119+
// RUN: %S/Inputs/ExtractOutputKey.py %t/keys.json %t/Foo.swiftinterface > %t/interface.casid
120+
// RUN: %target-swift-frontend -typecheck-module-from-interface %t/Foo.swiftinterface -disable-implicit-swift-modules \
121+
// RUN: -module-cache-path %t.module-cache -explicit-swift-module-map-file @%t/map.casid \
122+
// RUN: -cache-compile-job -cas-path %t/cas -allow-unstable-cache-key-for-testing -swift-version 5 -enable-library-evolution \
123+
// RUN: -explicit-interface-module-build -o /dev/null -Rcache-compile-job 2>&1 | %FileCheck %s --check-prefix=VERIFY-OUTPUT --check-prefix=CACHE-MISS
124+
// RUN: %target-swift-frontend -typecheck-module-from-interface %t/Foo.swiftinterface -disable-implicit-swift-modules \
125+
// RUN: -module-cache-path %t.module-cache -explicit-swift-module-map-file @%t/map.casid \
126+
// RUN: -cache-compile-job -cas-path %t/cas -allow-unstable-cache-key-for-testing -swift-version 5 -enable-library-evolution \
127+
// RUN: -explicit-interface-module-build -o %t/check.swiftmodule -Rcache-compile-job 2>&1 | %FileCheck %s --check-prefix=VERIFY-OUTPUT --check-prefix=CACHE-HIT
128+
113129
// CHECK-DAG: loaded module 'A'
114130
// CHECK-DAG: loaded module 'B'
115131
// CHECK-DAG: loaded module 'Swift'
116132
// CHECK-DAG: loaded module '_StringProcessing'
117133
// CHECK-DAG: loaded module '_Concurrency'
118134
// CHECK-DAG: loaded module 'SwiftOnoneSupport'
119135

136+
// CACHE-MISS: remark: cache miss output file
137+
// VERIFY-OUTPUT: warning: module 'A' was not compiled with library evolution support
138+
// CACHE-HIT: remark: replay output file
139+
120140
//--- A.swift
121141
func test() {}
122142

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: mkdir -p %t/clang-module-cache
3+
// RUN: mkdir -p %t/inputs
4+
// RUN: echo "/// Some cool comments" > %t/foo.swift
5+
// RUN: echo "public func foo() {}" >> %t/foo.swift
6+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/inputs/Foo.swiftmodule -emit-module-interface-path %t/Foo.swiftinterface \
7+
// RUN: -swift-version 5 -enable-library-evolution -module-cache-path %t.module-cache %t/foo.swift -module-name Foo
8+
9+
// RUN: echo "[{" > %/t/inputs/map.json
10+
// RUN: echo "\"moduleName\": \"Foo\"," >> %/t/inputs/map.json
11+
// RUN: echo "\"modulePath\": \"%/t/inputs/Foo.swiftmodule\"," >> %/t/inputs/map.json
12+
// RUN: echo "\"docPath\": \"%/t/inputs/Foo.swiftdoc\"," >> %/t/inputs/map.json
13+
// RUN: echo "\"sourceInfoPath\": \"%/t/inputs/Foo.swiftsourceinfo\"," >> %/t/inputs/map.json
14+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
15+
// RUN: echo "}," >> %/t/inputs/map.json
16+
// RUN: echo "{" >> %/t/inputs/map.json
17+
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
18+
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
19+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
20+
// RUN: echo "}," >> %/t/inputs/map.json
21+
// RUN: echo "{" >> %/t/inputs/map.json
22+
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
23+
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
24+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
25+
// RUN: echo "}," >> %/t/inputs/map.json
26+
// RUN: echo "{" >> %/t/inputs/map.json
27+
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
28+
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
29+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
30+
// RUN: echo "}," >> %/t/inputs/map.json
31+
// RUN: echo "{" >> %/t/inputs/map.json
32+
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
33+
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
34+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
35+
// RUN: echo "}]" >> %/t/inputs/map.json
36+
37+
// RUN: %target-swift-frontend -typecheck-module-from-interface %t/Foo.swiftinterface -module-cache-path %t.module-cache \
38+
// RUN: -o /dev/null -explicit-interface-module-build -explicit-swift-module-map-file %t/inputs/map.json -Rmodule-loading -Xcc -Rmodule-import 2>&1 | %FileCheck %s
39+
40+
// CHECK-DAG: loaded module 'Swift'
41+
// CHECK-DAG: loaded module '_StringProcessing'
42+
// CHECK-DAG: loaded module '_Concurrency'

0 commit comments

Comments
 (0)