Skip to content

[Serialization] Intro -Rmodule-recovery to remark on inconsistencies in swiftmodule files #66139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ ERROR(modularization_issue_decl_moved,Fatal,
(bool, DeclName, Identifier, Identifier))
ERROR(modularization_issue_decl_type_changed,Fatal,
"reference to %select{top-level|type}0 %1 broken by a context change; "
"the details of %1 %select{from %2|}5 changed since building '%3'"
"the declaration kind of %1 %select{from %2|}5 changed since building '%3'"
"%select{|, it was in %2 and is now found in %4}5",
(bool, DeclName, Identifier, StringRef, Identifier, bool))
ERROR(modularization_issue_decl_not_found,Fatal,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ namespace swift {
/// Emit a remark after loading a module.
bool EnableModuleLoadingRemarks = false;

/// Emit remarks about contextual inconsistencies in loaded modules.
bool EnableModuleRecoveryRemarks = false;

/// Emit a remark when indexing a system module.
bool EnableIndexingSystemModuleRemarks = false;

Expand Down
5 changes: 4 additions & 1 deletion include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,10 @@ def emit_cross_import_remarks : Flag<["-"], "Rcross-import">,

def remark_loading_module : Flag<["-"], "Rmodule-loading">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit a remark and file path of each loaded module">;
HelpText<"Emit remarks about loaded module">;
def remark_module_recovery : Flag<["-"], "Rmodule-recovery">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
HelpText<"Emit remarks about contextual inconsistencies in loaded modules">;

def remark_indexing_system_module : Flag<["-"], "Rindexing-system-module">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
Expand Down
1 change: 1 addition & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableCrossImportRemarks = Args.hasArg(OPT_emit_cross_import_remarks);

Opts.EnableModuleLoadingRemarks = Args.hasArg(OPT_remark_loading_module);
Opts.EnableModuleRecoveryRemarks = Args.hasArg(OPT_remark_module_recovery);

Opts.EnableIndexingSystemModuleRemarks = Args.hasArg(OPT_remark_indexing_system_module);

Expand Down
112 changes: 65 additions & 47 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,6 @@ void UnsafeDeserializationError::anchor() {}
const char ModularizationError::ID = '\0';
void ModularizationError::anchor() {}

static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error);

/// Skips a single record in the bitstream.
///
/// Destroys the stream position if the next entry is not a record.
Expand Down Expand Up @@ -234,21 +232,15 @@ void ExtensionError::diagnose(const ModuleFile *MF) const {
diag::modularization_issue_side_effect_extension_error);
}

llvm::Error ModuleFile::diagnoseFatal(llvm::Error error) const {

auto &ctx = getContext();
if (FileContext) {
if (ctx.LangOpts.EnableDeserializationRecovery) {
// Attempt to report relevant errors as diagnostics.
// At this time, only ModularizationErrors are reported directly. They
// can get here either directly or as underlying causes to a TypeError or
// and ExtensionError.
auto handleModularizationError =
[&](const ModularizationError &modularError) -> llvm::Error {
modularError.diagnose(this);
return llvm::Error::success();
};
error = llvm::handleErrors(std::move(error),
llvm::Error
ModuleFile::diagnoseModularizationError(llvm::Error error,
DiagnosticBehavior limit) const {
auto handleModularizationError =
[&](const ModularizationError &modularError) -> llvm::Error {
modularError.diagnose(this, limit);
return llvm::Error::success();
};
llvm::Error outError = llvm::handleErrors(std::move(error),
handleModularizationError,
[&](TypeError &typeError) -> llvm::Error {
if (typeError.diagnoseUnderlyingReason(handleModularizationError)) {
Expand All @@ -265,6 +257,16 @@ llvm::Error ModuleFile::diagnoseFatal(llvm::Error error) const {
return llvm::make_error<ExtensionError>(std::move(extError));
});

return outError;
}

llvm::Error ModuleFile::diagnoseFatal(llvm::Error error) const {

auto &ctx = getContext();
if (FileContext) {
if (ctx.LangOpts.EnableDeserializationRecovery) {
error = diagnoseModularizationError(std::move(error));

// If no error is left, it was reported as a diagnostic. There's no
// need to crash.
if (!error)
Expand Down Expand Up @@ -1460,7 +1462,7 @@ ModuleFile::getSubstitutionMapChecked(serialization::SubstitutionMapID id) {
for (auto typeID : replacementTypeIDs) {
auto typeOrError = getTypeChecked(typeID);
if (!typeOrError) {
consumeError(typeOrError.takeError());
diagnoseAndConsumeError(typeOrError.takeError());
continue;
}
replacementTypes.push_back(typeOrError.get());
Expand Down Expand Up @@ -1715,7 +1717,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
if (maybeType.errorIsA<FatalDeserializationError>())
return maybeType.takeError();
// FIXME: Don't throw away the inner error's information.
consumeError(maybeType.takeError());
diagnoseAndConsumeError(maybeType.takeError());
return llvm::make_error<XRefError>("couldn't decode type",
pathTrace, name);
}
Expand Down Expand Up @@ -2085,7 +2087,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
return maybeType.takeError();

// FIXME: Don't throw away the inner error's information.
consumeError(maybeType.takeError());
diagnoseAndConsumeError(maybeType.takeError());
return llvm::make_error<XRefError>("couldn't decode type",
pathTrace, memberName);
}
Expand Down Expand Up @@ -2924,7 +2926,7 @@ class DeclDeserializer {

auto maybeType = MF.getTypeChecked(typeID);
if (!maybeType) {
llvm::consumeError(maybeType.takeError());
MF.diagnoseAndConsumeError(maybeType.takeError());
continue;
}
inheritedTypes.push_back(
Expand Down Expand Up @@ -3250,10 +3252,10 @@ class DeclDeserializer {
return overriddenOrError.takeError();
} else if (MF.allowCompilerErrors()) {
// Drop overriding relationship when allowing errors.
llvm::consumeError(overriddenOrError.takeError());
MF.diagnoseAndConsumeError(overriddenOrError.takeError());
overridden = nullptr;
} else {
llvm::consumeError(overriddenOrError.takeError());
MF.diagnoseAndConsumeError(overriddenOrError.takeError());
if (overriddenAffectsABI || !ctx.LangOpts.EnableDeserializationRecovery) {
return llvm::make_error<OverrideError>(name, errorFlags,
numVTableEntries);
Expand Down Expand Up @@ -3397,7 +3399,7 @@ class DeclDeserializer {
if (overridden.errorIsA<FatalDeserializationError>())
return overridden.takeError();

llvm::consumeError(overridden.takeError());
MF.diagnoseAndConsumeError(overridden.takeError());

return llvm::make_error<OverrideError>(
name, getErrorFlags(), numVTableEntries);
Expand Down Expand Up @@ -3509,7 +3511,7 @@ class DeclDeserializer {

// FIXME: This is actually wrong. We can't just drop stored properties
// willy-nilly if the struct is @frozen.
consumeError(backingDecl.takeError());
MF.diagnoseAndConsumeError(backingDecl.takeError());
return var;
}

Expand Down Expand Up @@ -3726,15 +3728,15 @@ class DeclDeserializer {
overridden = overriddenOrError.get();
} else {
if (overriddenAffectsABI || !ctx.LangOpts.EnableDeserializationRecovery) {
llvm::consumeError(overriddenOrError.takeError());
MF.diagnoseAndConsumeError(overriddenOrError.takeError());
return llvm::make_error<OverrideError>(name, errorFlags,
numVTableEntries);
}
// Pass through deserialization errors.
if (overriddenOrError.errorIsA<FatalDeserializationError>())
return overriddenOrError.takeError();

llvm::consumeError(overriddenOrError.takeError());
MF.diagnoseAndConsumeError(overriddenOrError.takeError());
overridden = nullptr;
}

Expand Down Expand Up @@ -3999,7 +4001,7 @@ class DeclDeserializer {
if (!subMapOrError) {
// If the underlying type references internal details, ignore it.
auto unconsumedError =
consumeErrorIfXRefNonLoadedModule(subMapOrError.takeError());
MF.consumeExpectedError(subMapOrError.takeError());
if (unconsumedError)
return std::move(unconsumedError);
} else {
Expand Down Expand Up @@ -4056,7 +4058,7 @@ class DeclDeserializer {
return pattern.takeError();

// Silently drop the pattern...
llvm::consumeError(pattern.takeError());
MF.diagnoseAndConsumeError(pattern.takeError());
// ...but continue to read any further patterns we're expecting.
continue;
}
Expand Down Expand Up @@ -4574,7 +4576,7 @@ class DeclDeserializer {
// Pass through deserialization errors.
if (overridden.errorIsA<FatalDeserializationError>())
return overridden.takeError();
llvm::consumeError(overridden.takeError());
MF.diagnoseAndConsumeError(overridden.takeError());

DeclDeserializationError::Flags errorFlags;
return llvm::make_error<OverrideError>(
Expand Down Expand Up @@ -5098,7 +5100,7 @@ llvm::Error DeclDeserializer::deserializeCustomAttrs() {
// is safe to drop when it can't be deserialized.
// rdar://problem/56599179. When allowing errors we're doing a best
// effort to create a module, so ignore in that case as well.
consumeError(deserialized.takeError());
MF.diagnoseAndConsumeError(deserialized.takeError());
} else
return deserialized.takeError();
} else if (!deserialized.get() && MF.allowCompilerErrors()) {
Expand Down Expand Up @@ -6030,7 +6032,7 @@ Expected<Type> DESERIALIZE_TYPE(NAME_ALIAS_TYPE)(

// We're going to recover by falling back to the underlying type, so
// just ignore the error.
llvm::consumeError(aliasOrError.takeError());
MF.diagnoseAndConsumeError(aliasOrError.takeError());
}

if (!alias || !alias->getDeclaredInterfaceType()->isEqual(underlyingType)) {
Expand Down Expand Up @@ -7267,13 +7269,16 @@ Decl *handleErrorAndSupplyMissingProtoMember(ASTContext &context,
return suppliedMissingMember;
}

Decl *handleErrorAndSupplyMissingMiscMember(llvm::Error &&error) {
llvm::consumeError(std::move(error));
Decl *
ModuleFile::handleErrorAndSupplyMissingMiscMember(llvm::Error &&error) const {
diagnoseAndConsumeError(std::move(error));
return nullptr;
}

Decl *handleErrorAndSupplyMissingMember(ASTContext &context, Decl *container,
llvm::Error &&error) {
Decl *
ModuleFile::handleErrorAndSupplyMissingMember(ASTContext &context,
Decl *container,
llvm::Error &&error) const {
// Drop the member if it had a problem.
// FIXME: Handle overridable members in class extensions too, someday.
if (auto *containingClass = dyn_cast<ClassDecl>(container)) {
Expand Down Expand Up @@ -7347,14 +7352,14 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) {
}
}

static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) {
llvm::Error ModuleFile::consumeExpectedError(llvm::Error &&error) {
// Missing module errors are most likely caused by an
// implementation-only import hiding types and decls.
// rdar://problem/60291019
if (error.isA<XRefNonLoadedModuleError>() ||
error.isA<UnsafeDeserializationError>() ||
error.isA<ModularizationError>()) {
consumeError(std::move(error));
diagnoseAndConsumeError(std::move(error));
return llvm::Error::success();
}

Expand All @@ -7368,7 +7373,7 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) {
if (TE->underlyingReasonIsA<XRefNonLoadedModuleError>() ||
TE->underlyingReasonIsA<UnsafeDeserializationError>() ||
TE->underlyingReasonIsA<ModularizationError>()) {
consumeError(std::move(errorInfo));
diagnoseAndConsumeError(std::move(errorInfo));
return llvm::Error::success();
}

Expand All @@ -7378,6 +7383,19 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) {
return std::move(error);
}

void ModuleFile::diagnoseAndConsumeError(llvm::Error error) const {
auto &ctx = getContext();
if (ctx.LangOpts.EnableModuleRecoveryRemarks) {
error = diagnoseModularizationError(std::move(error),
DiagnosticBehavior::Remark);
// If error was already diagnosed it was also consumed.
if (!error)
return;
}

consumeError(std::move(error));
}

namespace {
class LazyConformanceLoaderInfo final
: llvm::TrailingObjects<LazyConformanceLoaderInfo,
Expand Down Expand Up @@ -7441,12 +7459,12 @@ ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData,

if (!conformance) {
auto unconsumedError =
consumeErrorIfXRefNonLoadedModule(conformance.takeError());
consumeExpectedError(conformance.takeError());
if (unconsumedError) {
// Ignore if allowing errors, it's just doing a best effort to produce
// *some* module anyway.
if (allowCompilerErrors())
consumeError(std::move(unconsumedError));
diagnoseAndConsumeError(std::move(unconsumedError));
else
fatal(std::move(unconsumedError));
}
Expand Down Expand Up @@ -7536,7 +7554,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
if (maybeConformance) {
reqConformances.push_back(maybeConformance.get());
} else if (allowCompilerErrors()) {
consumeError(maybeConformance.takeError());
diagnoseAndConsumeError(maybeConformance.takeError());
reqConformances.push_back(ProtocolConformanceRef::forInvalid());
} else {
fatal(maybeConformance.takeError());
Expand Down Expand Up @@ -7604,7 +7622,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
second = *secondOrError;
} else if (getContext().LangOpts.EnableDeserializationRecovery) {
second = ErrorType::get(getContext());
consumeError(secondOrError.takeError());
diagnoseAndConsumeError(secondOrError.takeError());
} else {
fatal(secondOrError.takeError());
}
Expand All @@ -7614,7 +7632,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
third = cast_or_null<TypeDecl>(*thirdOrError);
} else if (getContext().LangOpts.EnableDeserializationRecovery) {
third = nullptr;
consumeError(thirdOrError.takeError());
diagnoseAndConsumeError(thirdOrError.takeError());
} else {
fatal(thirdOrError.takeError());
}
Expand Down Expand Up @@ -7655,7 +7673,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
if (deserializedReq) {
req = cast_or_null<ValueDecl>(*deserializedReq);
} else if (getContext().LangOpts.EnableDeserializationRecovery) {
consumeError(deserializedReq.takeError());
diagnoseAndConsumeError(deserializedReq.takeError());
req = nullptr;
needToFillInOpaqueValueWitnesses = true;
} else {
Expand All @@ -7672,7 +7690,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
// In that case, we want the conformance to still be available, but
// we can't make use of the relationship to the underlying decl.
} else if (getContext().LangOpts.EnableDeserializationRecovery) {
consumeError(deserializedWitness.takeError());
diagnoseAndConsumeError(deserializedWitness.takeError());
isOpaque = true;
witness = nullptr;
} else {
Expand Down Expand Up @@ -7705,7 +7723,7 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
if (witnessSubstitutions.errorIsA<XRefNonLoadedModuleError>() ||
witnessSubstitutions.errorIsA<UnsafeDeserializationError>() ||
allowCompilerErrors()) {
consumeError(witnessSubstitutions.takeError());
diagnoseAndConsumeError(witnessSubstitutions.takeError());
isOpaque = true;
}
else
Expand Down
Loading