[Serialization] Save path traces from failed cross-references.

This is important information in a crash trace, so let's make sure to
preserve it even as the stack unwinds.
This commit is contained in:
Jordan Rose
2017-04-05 16:40:53 -07:00
parent 3dbc9de75b
commit e831dca955
3 changed files with 74 additions and 28 deletions

View File

@@ -549,6 +549,11 @@ ERROR(serialization_target_too_new_repl,none,
"module file's minimum deployment target is %0 v%1.%2%select{|.%3}3: %4",
(StringRef, unsigned, unsigned, unsigned, StringRef))
ERROR(serialization_fatal,Fatal,
"fatal error encountered while reading from module '%0'; "
"please file a bug report with your project and the crash log",
(StringRef))
ERROR(reserved_member_name,none,
"type member may not be named %0, since it would conflict with the"
" 'foo.%1' expression", (DeclName, StringRef))

View File

@@ -436,6 +436,10 @@ public:
return getStatus();
}
/// Emits one last diagnostic, logs the error, and then aborts for the stack
/// trace.
void fatal(llvm::Error error) LLVM_ATTRIBUTE_NORETURN;
ASTContext &getContext() const {
assert(FileContext && "no associated context yet");
return FileContext->getParentModule()->getASTContext();

View File

@@ -13,6 +13,7 @@
#include "swift/Serialization/ModuleFile.h"
#include "swift/Serialization/ModuleFormat.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/ForeignErrorConvention.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
@@ -95,7 +96,7 @@ namespace {
}
};
class PrettyXRefTrace : public llvm::PrettyStackTraceEntry {
class XRefTracePath {
class PathPiece {
public:
enum class Kind {
@@ -196,12 +197,11 @@ namespace {
}
};
private:
ModuleDecl &baseM;
SmallVector<PathPiece, 8> path;
public:
PrettyXRefTrace(ModuleDecl &M) : baseM(M) {}
explicit XRefTracePath(ModuleDecl &M) : baseM(M) {}
void addValue(Identifier name) {
path.push_back({ PathPiece::Kind::Value, name });
@@ -241,16 +241,27 @@ namespace {
path.pop_back();
}
void print(raw_ostream &os) const override {
void print(raw_ostream &os, StringRef leading = "") const {
os << "Cross-reference to module '" << baseM.getName() << "'\n";
for (auto &piece : path) {
os << "\t... ";
os << leading << "... ";
piece.print(os);
os << "\n";
}
}
};
class PrettyXRefTrace :
public llvm::PrettyStackTraceEntry,
public XRefTracePath {
public:
explicit PrettyXRefTrace(ModuleDecl &M) : XRefTracePath(M) {}
void print(raw_ostream &os) const override {
XRefTracePath::print(os, "\t");
}
};
class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
const char *Action;
const ModuleFile *MF;
@@ -262,6 +273,30 @@ namespace {
os << Action << " \'" << getNameOfModule(MF) << "'\n";
}
};
class XRefError : public llvm::ErrorInfo<XRefError> {
friend ErrorInfo;
static const char ID;
XRefTracePath path;
const char *message;
public:
template <size_t N>
XRefError(const char (&message)[N], XRefTracePath path)
: path(path), message(message) {}
void log(raw_ostream &OS) const override {
OS << message << "\n";
path.print(OS);
}
std::error_code convertToErrorCode() const override {
// This is a deprecated part of llvm::Error, so we just return a very
// generic value.
return {EINVAL, std::generic_category()};
}
};
const char XRefError::ID = '\0';
} // end anonymous namespace
@@ -286,6 +321,17 @@ static bool skipRecord(llvm::BitstreamCursor &cursor, unsigned recordKind) {
#endif
}
void ModuleFile::fatal(llvm::Error error) {
if (FileContext) {
getContext().Diags.diagnose(SourceLoc(), diag::serialization_fatal, Name);
}
logAllUnhandledErrors(std::move(error), llvm::errs(),
"\n*** DESERIALIZATION FAILURE (please include this "
"section in any bug report) ***\n");
abort();
}
ModuleFile &ModuleFile::getModuleFileForDelayedActions() {
assert(FileContext && "cannot delay actions before associating with a file");
ModuleDecl *associatedModule = getAssociatedModule();
@@ -1421,9 +1467,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
}
if (values.empty()) {
return llvm::make_error<llvm::StringError>(
"top-level value not found",
std::error_code(EINVAL, std::generic_category()));
return llvm::make_error<XRefError>("top-level value not found", pathTrace);
}
// Filters for values discovered in the remaining path pieces.
@@ -1542,18 +1586,16 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
pathTrace.addType(filterTy);
if (values.size() != 1) {
return llvm::make_error<llvm::StringError>(
"multiple matching base values",
std::error_code(EINVAL, std::generic_category()));
return llvm::make_error<XRefError>("multiple matching base values",
pathTrace);
}
auto nominal = dyn_cast<NominalTypeDecl>(values.front());
values.clear();
if (!nominal) {
return llvm::make_error<llvm::StringError>(
"base is not a nominal type",
std::error_code(EINVAL, std::generic_category()));
return llvm::make_error<XRefError>("base is not a nominal type",
pathTrace);
}
auto members = nominal->lookupDirect(memberName);
@@ -1642,9 +1684,8 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
case XREF_GENERIC_PARAM_PATH_PIECE: {
if (values.size() != 1) {
return llvm::make_error<llvm::StringError>(
"multiple matching base values",
std::error_code(EINVAL, std::generic_category()));
return llvm::make_error<XRefError>("multiple matching base values",
pathTrace);
}
uint32_t paramIndex;
@@ -1678,14 +1719,14 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
paramList = fn->getGenericParams();
if (!paramList) {
return llvm::make_error<llvm::StringError>(
return llvm::make_error<XRefError>(
"cross-reference to generic param for non-generic type",
std::error_code(EINVAL, std::generic_category()));
pathTrace);
}
if (paramIndex >= paramList->size()) {
return llvm::make_error<llvm::StringError>(
return llvm::make_error<XRefError>(
"generic argument index out of bounds",
std::error_code(EINVAL, std::generic_category()));
pathTrace);
}
values.clear();
@@ -1709,9 +1750,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
}
if (values.empty()) {
return llvm::make_error<llvm::StringError>(
"result not found",
std::error_code(EINVAL, std::generic_category()));
return llvm::make_error<XRefError>("result not found", pathTrace);
}
// Reset the module filter.
@@ -2156,8 +2195,7 @@ static uint64_t encodeLazyConformanceContextData(uint64_t numProtocols,
Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
Expected<Decl *> deserialized = getDeclChecked(DID, ForcedContext);
if (!deserialized) {
error();
return nullptr;
fatal(deserialized.takeError());
}
return deserialized.get();
}
@@ -3685,8 +3723,7 @@ Optional<swift::ResultConvention> getActualResultConvention(uint8_t raw) {
Type ModuleFile::getType(TypeID TID) {
Expected<Type> deserialized = getTypeChecked(TID);
if (!deserialized) {
error();
return Type();
fatal(deserialized.takeError());
}
return deserialized.get();
}