Skip to content

Commit 74fd209

Browse files
authored
Merge pull request #65713 from xymus/report-modularization-breaks
[Serialization] Report modularization breaks as proper diagnostics
2 parents ddbab22 + 144d7eb commit 74fd209

File tree

7 files changed

+357
-87
lines changed

7 files changed

+357
-87
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,27 @@ ERROR(need_hermetic_seal_to_import_module,none,
872872
"current compilation does not have -experimental-hermetic-seal-at-link",
873873
(Identifier))
874874

875+
ERROR(modularization_issue_decl_moved,Fatal,
876+
"reference to %select{top-level|type}0 %1 broken by a context change; "
877+
"%1 was expected to be in %2, but now a candidate is found only in %3",
878+
(bool, DeclName, Identifier, Identifier))
879+
ERROR(modularization_issue_decl_type_changed,Fatal,
880+
"reference to %select{top-level|type}0 %1 broken by a context change; "
881+
"the details of %1 %select{from %2|}5 changed since building '%3'"
882+
"%select{|, it was in %2 and is now found in %4}5",
883+
(bool, DeclName, Identifier, StringRef, Identifier, bool))
884+
ERROR(modularization_issue_decl_not_found,Fatal,
885+
"reference to %select{top-level|type}0 %1 broken by a context change; "
886+
"%1 is not found, it was expected to be in %2",
887+
(bool, DeclName, Identifier))
888+
889+
NOTE(modularization_issue_side_effect_extension_error,none,
890+
"could not deserialize extension",
891+
())
892+
NOTE(modularization_issue_side_effect_type_error,none,
893+
"could not deserialize type for %0",
894+
(DeclName))
895+
875896
ERROR(reserved_member_name,none,
876897
"type member must not be named %0, since it would conflict with the"
877898
" 'foo.%1' expression", (DeclName, StringRef))

lib/Serialization/Deserialization.cpp

Lines changed: 126 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ const char InvalidRecordKindError::ID = '\0';
158158
void InvalidRecordKindError::anchor() {}
159159
const char UnsafeDeserializationError::ID = '\0';
160160
void UnsafeDeserializationError::anchor() {}
161+
const char ModularizationError::ID = '\0';
162+
void ModularizationError::anchor() {}
161163

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

@@ -180,18 +182,108 @@ void ModuleFile::fatal(llvm::Error error) const {
180182
Core->fatal(diagnoseFatal(std::move(error)));
181183
}
182184

185+
SourceLoc ModuleFile::getSourceLoc() const {
186+
auto &SourceMgr = getContext().Diags.SourceMgr;
187+
auto filename = getModuleFilename();
188+
auto bufferID = SourceMgr.getIDForBufferIdentifier(filename);
189+
if (!bufferID)
190+
bufferID = SourceMgr.addMemBufferCopy(StringRef(), filename);
191+
return SourceMgr.getLocForBufferStart(*bufferID);
192+
}
193+
194+
void
195+
ModularizationError::diagnose(const ModuleFile *MF,
196+
DiagnosticBehavior limit) const {
197+
auto &ctx = MF->getContext();
198+
199+
auto diagnoseError = [&](Kind errorKind) {
200+
switch (errorKind) {
201+
case Kind::DeclMoved:
202+
return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_moved,
203+
declIsType, name, expectedModuleName,
204+
foundModuleName);
205+
case Kind::DeclKindChanged:
206+
return
207+
ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_type_changed,
208+
declIsType, name, expectedModuleName,
209+
referencedFromModuleName, foundModuleName,
210+
foundModuleName != expectedModuleName);
211+
case Kind::DeclNotFound:
212+
return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_not_found,
213+
declIsType, name, expectedModuleName);
214+
}
215+
llvm_unreachable("Unhandled ModularizationError::Kind in switch.");
216+
};
217+
218+
auto inFlight = diagnoseError(errorKind);
219+
inFlight.limitBehavior(limit);
220+
inFlight.flush();
221+
222+
// We could pass along the `path` information through notes.
223+
// However, for a top-level decl a path would just duplicate the
224+
// expected module name and the decl name from the diagnostic.
225+
}
226+
227+
void TypeError::diagnose(const ModuleFile *MF) const {
228+
MF->getContext().Diags.diagnose(MF->getSourceLoc(),
229+
diag::modularization_issue_side_effect_type_error,
230+
name);
231+
}
232+
233+
void ExtensionError::diagnose(const ModuleFile *MF) const {
234+
MF->getContext().Diags.diagnose(MF->getSourceLoc(),
235+
diag::modularization_issue_side_effect_extension_error);
236+
}
237+
183238
llvm::Error ModuleFile::diagnoseFatal(llvm::Error error) const {
184-
if (FileContext)
185-
getContext().Diags.diagnose(SourceLoc(), diag::serialization_fatal,
186-
Core->Name);
239+
240+
auto &ctx = getContext();
241+
if (FileContext) {
242+
if (ctx.LangOpts.EnableDeserializationRecovery) {
243+
// Attempt to report relevant errors as diagnostics.
244+
// At this time, only ModularizationErrors are reported directly. They
245+
// can get here either directly or as underlying causes to a TypeError or
246+
// and ExtensionError.
247+
auto handleModularizationError =
248+
[&](const ModularizationError &modularError) -> llvm::Error {
249+
modularError.diagnose(this);
250+
return llvm::Error::success();
251+
};
252+
error = llvm::handleErrors(std::move(error),
253+
handleModularizationError,
254+
[&](TypeError &typeError) -> llvm::Error {
255+
if (typeError.diagnoseUnderlyingReason(handleModularizationError)) {
256+
typeError.diagnose(this);
257+
return llvm::Error::success();
258+
}
259+
return llvm::make_error<TypeError>(std::move(typeError));
260+
},
261+
[&](ExtensionError &extError) -> llvm::Error {
262+
if (extError.diagnoseUnderlyingReason(handleModularizationError)) {
263+
extError.diagnose(this);
264+
return llvm::Error::success();
265+
}
266+
return llvm::make_error<ExtensionError>(std::move(extError));
267+
});
268+
269+
// If no error is left, it was reported as a diagnostic. There's no
270+
// need to crash.
271+
if (!error)
272+
return llvm::Error::success();
273+
}
274+
275+
// General deserialization failure message.
276+
ctx.Diags.diagnose(getSourceLoc(), diag::serialization_fatal, Core->Name);
277+
}
187278
// Unless in the debugger, crash. ModuleFileSharedCore::fatal() calls abort().
188279
// This allows aggregation of crash logs for compiler development, but in a
189280
// long-running process like LLDB this is undesirable. Only abort() if not in
190281
// the debugger.
191-
if (!getContext().LangOpts.DebuggerSupport)
282+
if (!ctx.LangOpts.DebuggerSupport)
192283
Core->fatal(std::move(error));
193284

194-
// Otherwise, augment the error with contextual information and pass it back.
285+
// Otherwise, augment the error with contextual information at this point
286+
// of failure and pass it back to be reported later.
195287
std::string msg;
196288
{
197289
llvm::raw_string_ostream os(msg);
@@ -1860,18 +1952,21 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
18601952
// is mostly for compiler engineers to understand a likely solution at a
18611953
// quick glance.
18621954
SmallVector<char, 64> strScratch;
1863-
SmallVector<std::string, 2> notes;
1864-
auto declName = getXRefDeclNameForError();
1955+
1956+
auto errorKind = ModularizationError::Kind::DeclNotFound;
1957+
Identifier foundIn;
1958+
bool isType = false;
1959+
18651960
if (recordID == XREF_TYPE_PATH_PIECE ||
18661961
recordID == XREF_VALUE_PATH_PIECE) {
18671962
auto &ctx = getContext();
18681963
for (auto nameAndModule : ctx.getLoadedModules()) {
1869-
auto baseModule = nameAndModule.second;
1964+
auto otherModule = nameAndModule.second;
18701965

18711966
IdentifierID IID;
18721967
IdentifierID privateDiscriminator = 0;
18731968
TypeID TID = 0;
1874-
bool isType = (recordID == XREF_TYPE_PATH_PIECE);
1969+
isType = (recordID == XREF_TYPE_PATH_PIECE);
18751970
bool inProtocolExt = false;
18761971
bool importedFromClang = false;
18771972
bool isStatic = false;
@@ -1895,10 +1990,10 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
18951990

18961991
values.clear();
18971992
if (privateDiscriminator) {
1898-
baseModule->lookupMember(values, baseModule, name,
1993+
otherModule->lookupMember(values, otherModule, name,
18991994
getIdentifier(privateDiscriminator));
19001995
} else {
1901-
baseModule->lookupQualified(baseModule, DeclNameRef(name),
1996+
otherModule->lookupQualified(otherModule, DeclNameRef(name),
19021997
NL_QualifiedDefault,
19031998
values);
19041999
}
@@ -1912,30 +2007,31 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
19122007
// Found a full match in a different module. It should be a different
19132008
// one because otherwise it would have succeeded on the first search.
19142009
// This is usually caused by the use of poorly modularized headers.
1915-
auto line = "There is a matching '" +
1916-
declName.getString(strScratch).str() +
1917-
"' in module '" +
1918-
std::string(nameAndModule.first.str()) +
1919-
"'. If this is imported from clang, please make sure " +
1920-
"the header is part of a single clang module.";
1921-
notes.emplace_back(line);
2010+
errorKind = ModularizationError::Kind::DeclMoved;
2011+
foundIn = otherModule->getName();
2012+
break;
19222013
} else if (hadAMatchBeforeFiltering) {
19232014
// Found a match that was filtered out. This may be from the same
19242015
// expected module if there's a type difference. This can be caused
19252016
// by the use of different Swift language versions between a library
19262017
// with serialized SIL and a client.
1927-
auto line = "'" +
1928-
declName.getString(strScratch).str() +
1929-
"' in module '" +
1930-
std::string(nameAndModule.first.str()) +
1931-
"' was filtered out.";
1932-
notes.emplace_back(line);
2018+
errorKind = ModularizationError::Kind::DeclKindChanged;
2019+
foundIn = otherModule->getName();
2020+
break;
19332021
}
19342022
}
19352023
}
19362024

1937-
return llvm::make_error<XRefError>("top-level value not found", pathTrace,
1938-
declName, notes);
2025+
auto declName = getXRefDeclNameForError();
2026+
auto expectedIn = baseModule->getName();
2027+
auto referencedFrom = getName();
2028+
return llvm::make_error<ModularizationError>(declName,
2029+
isType,
2030+
errorKind,
2031+
expectedIn,
2032+
referencedFrom,
2033+
foundIn,
2034+
pathTrace);
19392035
}
19402036

19412037
// Filters for values discovered in the remaining path pieces.
@@ -7340,7 +7436,8 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) {
73407436
// implementation-only import hiding types and decls.
73417437
// rdar://problem/60291019
73427438
if (error.isA<XRefNonLoadedModuleError>() ||
7343-
error.isA<UnsafeDeserializationError>()) {
7439+
error.isA<UnsafeDeserializationError>() ||
7440+
error.isA<ModularizationError>()) {
73447441
consumeError(std::move(error));
73457442
return llvm::Error::success();
73467443
}
@@ -7353,7 +7450,8 @@ static llvm::Error consumeErrorIfXRefNonLoadedModule(llvm::Error &&error) {
73537450
auto *TE = static_cast<TypeError*>(errorInfo.get());
73547451

73557452
if (TE->underlyingReasonIsA<XRefNonLoadedModuleError>() ||
7356-
TE->underlyingReasonIsA<UnsafeDeserializationError>()) {
7453+
TE->underlyingReasonIsA<UnsafeDeserializationError>() ||
7454+
TE->underlyingReasonIsA<ModularizationError>()) {
73577455
consumeError(std::move(errorInfo));
73587456
return llvm::Error::success();
73597457
}

0 commit comments

Comments
 (0)