Serialization: Skip invalid decls during module serialization.

When `-enable-lazy-typecheck` is specified, serialization may be expected to
run on an AST containing invalid declarations since type checking may happen
on-demand, during serialization, in this mode. If the declarations that are
invalid are not skipped, then the compiler is likely to crash when attempting
to serialize them. Now, invalid declarations are skipped and an error is
emitted at the end of serialization to note that serialization failed.

Additionally, a new `-Rmodule-serialization` flag can be specified to request
more detailed information about module serialization failures. This would be
useful in a situation where lazy typechecking does not produce any diagnostic
for some reason, but module serialization fails and more information is
therefore required to debug.

Resolves rdar://123260476
This commit is contained in:
Allan Shortlidge
2024-02-19 14:31:24 -08:00
parent d4c54af247
commit 72da8f4e9c
9 changed files with 71 additions and 7 deletions

View File

@@ -808,6 +808,13 @@ WARNING(sema_import_current_module_with_file,none,
ERROR(sema_opening_import,Fatal, ERROR(sema_opening_import,Fatal,
"opening import file for module %0: %1", (Identifier, StringRef)) "opening import file for module %0: %1", (Identifier, StringRef))
REMARK(serialization_skipped_invalid_decl,none,
"serialization skipped invalid %kind0",
(const Decl *))
ERROR(serialization_failed,none,
"serialization of module %0 failed due to the errors above",
(const ModuleDecl *))
ERROR(serialization_load_failed,Fatal, ERROR(serialization_load_failed,Fatal,
"failed to load module '%0'", (StringRef)) "failed to load module '%0'", (StringRef))
ERROR(module_interface_build_failed,Fatal, ERROR(module_interface_build_failed,Fatal,

View File

@@ -243,6 +243,9 @@ namespace swift {
/// Emit remarks about contextual inconsistencies in loaded modules. /// Emit remarks about contextual inconsistencies in loaded modules.
bool EnableModuleRecoveryRemarks = false; bool EnableModuleRecoveryRemarks = false;
/// Emit remarks for unexpected conditions when serializing a module.
bool EnableModuleSerializationRemarks = false;
/// Emit remarks about the source of each element exposed by the module API. /// Emit remarks about the source of each element exposed by the module API.
bool EnableModuleApiImportRemarks = false; bool EnableModuleApiImportRemarks = false;

View File

@@ -417,6 +417,10 @@ def remark_skip_explicit_interface_build : Flag<["-"], "Rskip-explicit-interface
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit a remark if an explicit module interface invocation has an early exit because the expected output is up-to-date">; HelpText<"Emit a remark if an explicit module interface invocation has an early exit because the expected output is up-to-date">;
def remark_module_serialization : Flag<["-"], "Rmodule-serialization">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit remarks about module serialization">;
def emit_tbd : Flag<["-"], "emit-tbd">, def emit_tbd : Flag<["-"], "emit-tbd">,
HelpText<"Emit a TBD file">, HelpText<"Emit a TBD file">,
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>; Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>;

View File

@@ -159,6 +159,7 @@ namespace swift {
bool IsOSSA = false; bool IsOSSA = false;
bool SkipNonExportableDecls = false; bool SkipNonExportableDecls = false;
bool ExplicitModuleBuild = false; bool ExplicitModuleBuild = false;
bool EnableSerializationRemarks = false;
}; };
} // end namespace swift } // end namespace swift

View File

@@ -1088,6 +1088,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableModuleLoadingRemarks = Args.hasArg(OPT_remark_loading_module); Opts.EnableModuleLoadingRemarks = Args.hasArg(OPT_remark_loading_module);
Opts.EnableModuleRecoveryRemarks = Args.hasArg(OPT_remark_module_recovery); Opts.EnableModuleRecoveryRemarks = Args.hasArg(OPT_remark_module_recovery);
Opts.EnableModuleSerializationRemarks =
Args.hasArg(OPT_remark_module_serialization);
Opts.EnableModuleApiImportRemarks = Args.hasArg(OPT_remark_module_api_import); Opts.EnableModuleApiImportRemarks = Args.hasArg(OPT_remark_module_api_import);
Opts.EnableMacroLoadingRemarks = Args.hasArg(OPT_remark_macro_loading); Opts.EnableMacroLoadingRemarks = Args.hasArg(OPT_remark_macro_loading);
Opts.EnableIndexingSystemModuleRemarks = Args.hasArg(OPT_remark_indexing_system_module); Opts.EnableIndexingSystemModuleRemarks = Args.hasArg(OPT_remark_indexing_system_module);

View File

@@ -253,6 +253,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions(
serializationOpts.ExplicitModuleBuild = FrontendOpts.DisableImplicitModules; serializationOpts.ExplicitModuleBuild = FrontendOpts.DisableImplicitModules;
serializationOpts.EnableSerializationRemarks =
getLangOptions().EnableModuleSerializationRemarks;
return serializationOpts; return serializationOpts;
} }

View File

@@ -18,6 +18,7 @@
#include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTVisitor.h"
#include "swift/AST/AutoDiff.h" #include "swift/AST/AutoDiff.h"
#include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/DiagnosticsCommon.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Expr.h" #include "swift/AST/Expr.h"
#include "swift/AST/FileSystem.h" #include "swift/AST/FileSystem.h"
#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignAsyncConvention.h"
@@ -4998,13 +4999,8 @@ void Serializer::writeASTBlockEntity(const Decl *D) {
PrettyStackTraceDecl trace("serializing", D); PrettyStackTraceDecl trace("serializing", D);
assert(DeclsToSerialize.hasRef(D)); assert(DeclsToSerialize.hasRef(D));
if (D->isInvalid()) { if (skipDeclIfInvalid(D))
assert(allowCompilerErrors() && return;
"cannot create a module with an invalid decl");
if (canSkipWhenInvalid(D))
return;
}
BitOffset initialOffset = Out.GetCurrentBitNo(); BitOffset initialOffset = Out.GetCurrentBitNo();
SWIFT_DEFER { SWIFT_DEFER {
@@ -6782,6 +6778,11 @@ void Serializer::writeToStream(
S.writeInputBlock(); S.writeInputBlock();
S.writeSIL(SILMod, options.SerializeAllSIL); S.writeSIL(SILMod, options.SerializeAllSIL);
S.writeAST(DC); S.writeAST(DC);
if (S.hadError)
S.getASTContext().Diags.diagnose(SourceLoc(), diag::serialization_failed,
S.M);
if (!options.DisableCrossModuleIncrementalInfo && DepGraph) { if (!options.DisableCrossModuleIncrementalInfo && DepGraph) {
fine_grained_dependencies::writeFineGrainedDependencyGraph( fine_grained_dependencies::writeFineGrainedDependencyGraph(
S.Out, *DepGraph, fine_grained_dependencies::Purpose::ForSwiftModule); S.Out, *DepGraph, fine_grained_dependencies::Purpose::ForSwiftModule);
@@ -6795,6 +6796,22 @@ bool Serializer::allowCompilerErrors() const {
return getASTContext().LangOpts.AllowModuleWithCompilerErrors; return getASTContext().LangOpts.AllowModuleWithCompilerErrors;
} }
bool Serializer::skipDeclIfInvalid(const Decl *decl) {
if (!decl->isInvalid())
return false;
if (allowCompilerErrors())
return canSkipWhenInvalid(decl);
if (Options.EnableSerializationRemarks) {
getASTContext().Diags.diagnose(
decl->getLoc(), diag::serialization_skipped_invalid_decl, decl);
}
hadError = true;
return true;
}
void serialization::writeToStream( void serialization::writeToStream(
raw_ostream &os, ModuleOrSourceFile DC, const SILModule *M, raw_ostream &os, ModuleOrSourceFile DC, const SILModule *M,
const SerializationOptions &options, const SerializationOptions &options,

View File

@@ -112,6 +112,10 @@ class Serializer : public SerializerBase {
SmallVector<DeclID, 16> exportedPrespecializationDecls; SmallVector<DeclID, 16> exportedPrespecializationDecls;
/// Will be set to true if any serialization step failed, for example due to
/// an error in the AST.
bool hadError = false;
/// Helper for serializing entities in the AST block object graph. /// Helper for serializing entities in the AST block object graph.
/// ///
/// Keeps track of assigning IDs to newly-seen entities, and collecting /// Keeps track of assigning IDs to newly-seen entities, and collecting
@@ -570,6 +574,11 @@ public:
void writePrimaryAssociatedTypes(ArrayRef<AssociatedTypeDecl *> assocTypes); void writePrimaryAssociatedTypes(ArrayRef<AssociatedTypeDecl *> assocTypes);
bool allowCompilerErrors() const; bool allowCompilerErrors() const;
private:
/// If the declaration is invalid, records that an error occurred and returns
/// true if the decl should be skipped.
bool skipDeclIfInvalid(const Decl *decl);
}; };
} // end namespace serialization } // end namespace serialization

View File

@@ -0,0 +1,18 @@
// Verify the -emit-module job fails with a broken AST.
// RUN: not %target-swift-frontend -emit-module %s -emit-module-path /dev/null -module-name lazy_typecheck -swift-version 5 -enable-library-evolution -parse-as-library -experimental-lazy-typecheck -experimental-skip-all-function-bodies -experimental-skip-non-exportable-decls 2>&1 | %FileCheck %s
// CHECK: <unknown>:0: error: serialization of module 'lazy_typecheck' failed due to the errors above
// Verify typechecking errors are emitted.
// RUN: %target-swift-frontend -emit-module %s -emit-module-path /dev/null -module-name lazy_typecheck -swift-version 5 -enable-library-evolution -parse-as-library -experimental-lazy-typecheck -experimental-skip-all-function-bodies -experimental-skip-non-exportable-decls -verify -verify-ignore-unknown
// Verify output with -Rmodule-serialization.
// RUN: %target-swift-frontend -emit-module %s -emit-module-path /dev/null -module-name lazy_typecheck -swift-version 5 -enable-library-evolution -parse-as-library -experimental-lazy-typecheck -experimental-skip-all-function-bodies -experimental-skip-non-exportable-decls -verify -verify-ignore-unknown -Rmodule-serialization -verify-additional-prefix serialization-
public func returnsInvalidType() -> InvalidType { fatalError() }
// expected-error@-1 {{cannot find type 'InvalidType' in scope}}
// expected-serialization-remark@-2 {{serialization skipped invalid global function 'returnsInvalidType()'}}
public func takesInvalidType(_ x: InvalidType) {}
// expected-error@-1 {{cannot find type 'InvalidType' in scope}}
// expected-serialization-remark@-2 {{serialization skipped invalid global function 'takesInvalidType'}}