[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", "module file's minimum deployment target is %0 v%1.%2%select{|.%3}3: %4",
(StringRef, unsigned, unsigned, unsigned, StringRef)) (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, ERROR(reserved_member_name,none,
"type member may not be named %0, since it would conflict with the" "type member may not be named %0, since it would conflict with the"
" 'foo.%1' expression", (DeclName, StringRef)) " 'foo.%1' expression", (DeclName, StringRef))

View File

@@ -436,6 +436,10 @@ public:
return getStatus(); 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 { ASTContext &getContext() const {
assert(FileContext && "no associated context yet"); assert(FileContext && "no associated context yet");
return FileContext->getParentModule()->getASTContext(); return FileContext->getParentModule()->getASTContext();

View File

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