Skip to content

Commit f410958

Browse files
committed
[Serialization] Import incompatible targets when allowing errors
If allowing modules to be output with compile errors (-experimental-allow-module-with-errors), import targets regardless of whether they are compatible or not, and still output the module. The error diagnostic will still be output (preventing SILGen), but the AST will be available for various editor functionality.
1 parent 44ed14e commit f410958

File tree

6 files changed

+67
-31
lines changed

6 files changed

+67
-31
lines changed

lib/Serialization/ModuleFile.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@ ModuleFile::ModuleFile(std::shared_ptr<const ModuleFileSharedCore> core)
118118
allocateBuffer(Identifiers, core->Identifiers);
119119
}
120120

121-
Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) {
121+
Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
122+
bool recoverFromIncompatibility) {
122123
PrettyStackTraceModuleFile stackEntry(*this);
123124

124125
assert(!hasError() && "error already detected; should not call this");
125126
assert(!FileContext && "already associated with an AST module");
126127
FileContext = file;
128+
Status status = Status::Valid;
127129

128130
ModuleDecl *M = file->getParentModule();
129131
if (M->getName().str() != Core->Name)
@@ -134,12 +136,14 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) {
134136
llvm::Triple moduleTarget(llvm::Triple::normalize(Core->TargetTriple));
135137
if (!areCompatibleArchitectures(moduleTarget, ctx.LangOpts.Target) ||
136138
!areCompatibleOSs(moduleTarget, ctx.LangOpts.Target)) {
137-
return error(Status::TargetIncompatible);
138-
}
139-
if (ctx.LangOpts.EnableTargetOSChecking &&
140-
!M->isResilient() &&
141-
isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) {
142-
return error(Status::TargetTooNew);
139+
status = Status::TargetIncompatible;
140+
if (!recoverFromIncompatibility)
141+
return error(status);
142+
} else if (ctx.LangOpts.EnableTargetOSChecking && !M->isResilient() &&
143+
isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) {
144+
status = Status::TargetTooNew;
145+
if (!recoverFromIncompatibility)
146+
return error(status);
143147
}
144148

145149
for (const auto &searchPath : Core->SearchPaths)
@@ -240,7 +244,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) {
240244
None);
241245
}
242246

243-
return Status::Valid;
247+
return status;
244248
}
245249

246250
bool ModuleFile::mayHaveDiagnosticsPointingAtBuffer() const {

lib/Serialization/ModuleFile.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,14 @@ class ModuleFile
486486
/// This does not include diagnostics about \e this file failing to load,
487487
/// but rather other things that might be imported as part of bringing the
488488
/// file into the AST.
489+
/// \param recoverFromIncompatibility Whether to associate the file
490+
/// regardless of the compatibility with the AST module. Still returns the
491+
/// underlying error for diagnostic purposes but does not set the error bit.
489492
///
490493
/// \returns any error that occurred during association, such as being
491494
/// compiled for a different OS.
492-
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc);
495+
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
496+
bool recoverFromIncompatibility);
493497

494498
/// Returns `true` if there is a buffer that might contain source code where
495499
/// other parts of the compiler could have emitted diagnostics, to indicate

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -706,13 +706,15 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
706706
std::move(moduleDocInputBuffer),
707707
std::move(moduleSourceInfoInputBuffer),
708708
isFramework, loadedModuleFileCore);
709+
SerializedASTFile *fileUnit = nullptr;
710+
709711
if (loadInfo.status == serialization::Status::Valid) {
710712
loadedModuleFile =
711713
std::make_unique<ModuleFile>(std::move(loadedModuleFileCore));
712714
M.setResilienceStrategy(loadedModuleFile->getResilienceStrategy());
713715

714716
// We've loaded the file. Now try to bring it into the AST.
715-
auto fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile);
717+
fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile);
716718
if (loadedModuleFile->isTestable())
717719
M.setTestingEnabled();
718720
if (loadedModuleFile->arePrivateImportsEnabled())
@@ -723,8 +725,8 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
723725
M.setHasIncrementalInfo();
724726

725727
auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc());
726-
loadInfo.status =
727-
loadedModuleFile->associateWithFileContext(fileUnit, diagLocOrInvalid);
728+
loadInfo.status = loadedModuleFile->associateWithFileContext(
729+
fileUnit, diagLocOrInvalid, Ctx.LangOpts.AllowModuleWithCompilerErrors);
728730

729731
// FIXME: This seems wrong. Overlay for system Clang module doesn't
730732
// necessarily mean it's "system" module. User can make their own overlay
@@ -734,31 +736,36 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
734736
if (shadowed->isSystemModule())
735737
M.setIsSystemModule(true);
736738

737-
if (loadInfo.status == serialization::Status::Valid) {
739+
if (loadInfo.status == serialization::Status::Valid ||
740+
(Ctx.LangOpts.AllowModuleWithCompilerErrors &&
741+
(loadInfo.status == serialization::Status::TargetTooNew ||
742+
loadInfo.status == serialization::Status::TargetIncompatible))) {
738743
Ctx.bumpGeneration();
739744
LoadedModuleFiles.emplace_back(std::move(loadedModuleFile),
740745
Ctx.getCurrentGeneration());
741746
findOverlayFiles(diagLoc.getValueOr(SourceLoc()), &M, fileUnit);
742-
return fileUnit;
747+
} else {
748+
fileUnit = nullptr;
743749
}
744750
}
745751

746-
// From here on is the failure path.
747-
748-
if (diagLoc)
749-
serialization::diagnoseSerializedASTLoadFailure(
750-
Ctx, *diagLoc, loadInfo, moduleBufferID,
751-
moduleDocBufferID, loadedModuleFile.get(), M.getName());
752-
753-
// Even though the module failed to load, it's possible its contents include
754-
// a source buffer that need to survive because it's already been used for
755-
// diagnostics.
756-
// Note this is only necessary in case a bridging header failed to load
757-
// during the `associateWithFileContext()` call.
758-
if (loadedModuleFile && loadedModuleFile->mayHaveDiagnosticsPointingAtBuffer())
759-
OrphanedModuleFiles.push_back(std::move(loadedModuleFile));
752+
if (loadInfo.status != serialization::Status::Valid) {
753+
if (diagLoc)
754+
serialization::diagnoseSerializedASTLoadFailure(
755+
Ctx, *diagLoc, loadInfo, moduleBufferID, moduleDocBufferID,
756+
loadedModuleFile.get(), M.getName());
757+
758+
// Even though the module failed to load, it's possible its contents
759+
// include a source buffer that need to survive because it's already been
760+
// used for diagnostics.
761+
// Note this is only necessary in case a bridging header failed to load
762+
// during the `associateWithFileContext()` call.
763+
if (loadedModuleFile &&
764+
loadedModuleFile->mayHaveDiagnosticsPointingAtBuffer())
765+
OrphanedModuleFiles.push_back(std::move(loadedModuleFile));
766+
}
760767

761-
return nullptr;
768+
return fileUnit;
762769
}
763770

764771
void swift::serialization::diagnoseSerializedASTLoadFailure(
@@ -908,7 +915,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
908915
// FIXME: This doesn't handle a non-debugger REPL, which should also treat
909916
// this as a non-fatal error.
910917
auto diagKind = diag::serialization_target_incompatible;
911-
if (Ctx.LangOpts.DebuggerSupport)
918+
if (Ctx.LangOpts.DebuggerSupport ||
919+
Ctx.LangOpts.AllowModuleWithCompilerErrors)
912920
diagKind = diag::serialization_target_incompatible_repl;
913921
Ctx.Diags.diagnose(diagLoc, diagKind, ModuleName, loadInfo.targetTriple,
914922
moduleBufferID);
@@ -926,7 +934,8 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
926934
// FIXME: This doesn't handle a non-debugger REPL, which should also treat
927935
// this as a non-fatal error.
928936
auto diagKind = diag::serialization_target_too_new;
929-
if (Ctx.LangOpts.DebuggerSupport)
937+
if (Ctx.LangOpts.DebuggerSupport ||
938+
Ctx.LangOpts.AllowModuleWithCompilerErrors)
930939
diagKind = diag::serialization_target_too_new_repl;
931940
Ctx.Diags.diagnose(diagLoc, diagKind, compilationOSInfo.first,
932941
compilationOSInfo.second, ModuleName,

test/Serialization/target-incompatible.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
// RUN: %{python} -u %S/Inputs/binary_sub.py x86_64-unknown-darwin14 x86_64-unknown-solaris8 < %t/solaris-template.swiftmodule > %t/solaris.swiftmodule
99
// RUN: not %target-swift-frontend -I %t -typecheck -parse-stdlib %s -DSOLARIS 2>&1 | %FileCheck -check-prefix=CHECK-SOLARIS %s
1010

11+
// Check that we still get the diagnostic but the module is output anyway when
12+
// allowing errors
13+
// RUN: %target-swift-frontend -I %t -parse-stdlib -experimental-allow-module-with-compiler-errors -emit-module -module-name incompatmips -o %t %s -DMIPS 2>&1 | %FileCheck -check-prefix=CHECK-MIPS %s
14+
// RUN: ls %t/incompatmips.swiftmodule
15+
// RUN: %target-swift-frontend -I %t -parse-stdlib -experimental-allow-module-with-compiler-errors -emit-module -module-name incompatsol -o %t %s -DSOLARIS 2>&1 | %FileCheck -check-prefix=CHECK-SOLARIS %s
16+
// RUN: ls %t/incompatsol.swiftmodule
17+
1118
// These checks should still hold with -enable-library-evolution.
1219

1320
// RUN: %swift -target x86_64-unknown-darwin14 -o %t/mips-template.swiftmodule -parse-stdlib -emit-module -module-name mips %S/../Inputs/empty.swift -enable-library-evolution

test/Serialization/target-too-new-ios.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
// RUN: %target-swift-frontend -parse-stdlib -target x86_64-apple-ios50.50.1 -I %t -typecheck %s
66
// RUN: %target-swift-frontend -parse-stdlib -target x86_64-apple-ios50.51 -I %t -typecheck %s
77

8+
// Check that we still get the diagnostic but the module is output anyway when
9+
// allowing errors
10+
// RUN: %target-swift-frontend -I %t -target x86_64-apple-ios12 -parse-stdlib -experimental-allow-module-with-compiler-errors -emit-module -module-name toonewios -o %t %s 2>&1 | %FileCheck %s
11+
// RUN: ls %t/toonewios.swiftmodule
12+
813
// REQUIRES: OS=ios
914

1015
// CHECK: :[[@LINE+1]]:8: error: compiling for iOS 12.0, but module 'empty' has a minimum deployment target of iOS 50.50.1: {{.*}}empty.swiftmodule{{$}}

test/Serialization/target-too-new.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.50 -I %t -typecheck %s
77
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.50.1 -I %t -typecheck %s
88

9+
// Check that we still get the diagnostic but the module is output anyway when
10+
// allowing errors
11+
// RUN: %target-swift-frontend -I %t -target %target-cpu-apple-macosx10.9 -parse-stdlib -experimental-allow-module-with-compiler-errors -emit-module -module-name toonew -o %t %s 2>&1 | %FileCheck %s
12+
// RUN: ls %t/toonew.swiftmodule
13+
// RUN: %target-swift-frontend -I %t -target %target-cpu-apple-darwin13 -parse-stdlib -experimental-allow-module-with-compiler-errors -emit-module -module-name toonewother -o %t %s 2>&1 | %FileCheck %s
14+
// RUN: ls %t/toonewother.swiftmodule
15+
916
// Allow any version when built with resilience. (Really we should encode a
1017
// "minimum supported OS", but we don't have that information today.)
1118
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx10.50 -emit-module -parse-stdlib %S/../Inputs/empty.swift -enable-library-evolution -o %t

0 commit comments

Comments
 (0)