Skip to content

Start recovering from missing types in SIL deserialization #17564

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 2 commits into from
Jun 28, 2018
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
83 changes: 40 additions & 43 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,6 @@ void TypeError::anchor() {}
const char ExtensionError::ID = '\0';
void ExtensionError::anchor() {}

LLVM_NODISCARD
static std::unique_ptr<llvm::ErrorInfoBase> takeErrorInfo(llvm::Error error) {
std::unique_ptr<llvm::ErrorInfoBase> result;
llvm::handleAllErrors(std::move(error),
[&](std::unique_ptr<llvm::ErrorInfoBase> info) {
result = std::move(info);
});
return result;
}


/// Skips a single record in the bitstream.
///
/// Returns true if the next entry is a record of type \p recordKind.
Expand Down Expand Up @@ -4756,27 +4745,42 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
}

auto processParameter = [&](TypeID typeID, uint64_t rawConvention)
-> Optional<SILParameterInfo> {
-> llvm::Expected<SILParameterInfo> {
auto convention = getActualParameterConvention(rawConvention);
auto type = getType(typeID);
if (!convention || !type) return None;
return SILParameterInfo(type->getCanonicalType(), *convention);
if (!convention) {
error();
llvm_unreachable("an error is a fatal exit at this point");
}
auto type = getTypeChecked(typeID);
if (!type)
return type.takeError();
return SILParameterInfo(type.get()->getCanonicalType(), *convention);
};

auto processYield = [&](TypeID typeID, uint64_t rawConvention)
-> Optional<SILYieldInfo> {
-> llvm::Expected<SILYieldInfo> {
auto convention = getActualParameterConvention(rawConvention);
auto type = getType(typeID);
if (!convention || !type) return None;
return SILYieldInfo(type->getCanonicalType(), *convention);
if (!convention) {
error();
llvm_unreachable("an error is a fatal exit at this point");
}
auto type = getTypeChecked(typeID);
if (!type)
return type.takeError();
return SILYieldInfo(type.get()->getCanonicalType(), *convention);
};

auto processResult = [&](TypeID typeID, uint64_t rawConvention)
-> Optional<SILResultInfo> {
-> llvm::Expected<SILResultInfo> {
auto convention = getActualResultConvention(rawConvention);
auto type = getType(typeID);
if (!convention || !type) return None;
return SILResultInfo(type->getCanonicalType(), *convention);
if (!convention) {
error();
llvm_unreachable("an error is a fatal exit at this point");
}
auto type = getTypeChecked(typeID);
if (!type)
return type.takeError();
return SILResultInfo(type.get()->getCanonicalType(), *convention);
};

// Bounds check. FIXME: overflow
Expand All @@ -4795,11 +4799,9 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
auto typeID = variableData[nextVariableDataIndex++];
auto rawConvention = variableData[nextVariableDataIndex++];
auto param = processParameter(typeID, rawConvention);
if (!param) {
error();
return nullptr;
}
allParams.push_back(*param);
if (!param)
return param.takeError();
allParams.push_back(param.get());
}

// Process the yields.
Expand All @@ -4809,11 +4811,9 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
auto typeID = variableData[nextVariableDataIndex++];
auto rawConvention = variableData[nextVariableDataIndex++];
auto yield = processYield(typeID, rawConvention);
if (!yield) {
error();
return nullptr;
}
allYields.push_back(*yield);
if (!yield)
return yield.takeError();
allYields.push_back(yield.get());
}

// Process the results.
Expand All @@ -4823,23 +4823,20 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
auto typeID = variableData[nextVariableDataIndex++];
auto rawConvention = variableData[nextVariableDataIndex++];
auto result = processResult(typeID, rawConvention);
if (!result) {
error();
return nullptr;
}
allResults.push_back(*result);
if (!result)
return result.takeError();
allResults.push_back(result.get());
}

// Process the error result.
Optional<SILResultInfo> errorResult;
if (hasErrorResult) {
auto typeID = variableData[nextVariableDataIndex++];
auto rawConvention = variableData[nextVariableDataIndex++];
errorResult = processResult(typeID, rawConvention);
if (!errorResult) {
error();
return nullptr;
}
auto maybeErrorResult = processResult(typeID, rawConvention);
if (!maybeErrorResult)
return maybeErrorResult.takeError();
errorResult = maybeErrorResult.get();
}

Optional<ProtocolConformanceRef> witnessMethodConformance;
Expand Down
35 changes: 35 additions & 0 deletions lib/Serialization/DeserializationErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,41 @@ class ExtensionError : public llvm::ErrorInfo<ExtensionError> {
}
};

class SILEntityError : public llvm::ErrorInfo<SILEntityError> {
friend ErrorInfo;
static const char ID;
void anchor() override;

std::unique_ptr<ErrorInfoBase> underlyingReason;
StringRef name;
public:
SILEntityError(StringRef name, std::unique_ptr<ErrorInfoBase> reason)
: underlyingReason(std::move(reason)), name(name) {}

void log(raw_ostream &OS) const override {
OS << "could not deserialize SIL entity '" << name << "'";
if (underlyingReason) {
OS << ": ";
underlyingReason->log(OS);
}
}

std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};

LLVM_NODISCARD
static inline std::unique_ptr<llvm::ErrorInfoBase>
takeErrorInfo(llvm::Error error) {
std::unique_ptr<llvm::ErrorInfoBase> result;
llvm::handleAllErrors(std::move(error),
[&](std::unique_ptr<llvm::ErrorInfoBase> info) {
result = std::move(info);
});
return result;
}

class PrettyStackTraceModuleFile : public llvm::PrettyStackTraceEntry {
const char *Action;
const ModuleFile &MF;
Expand Down
93 changes: 78 additions & 15 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@

#define DEBUG_TYPE "deserialize"
#include "DeserializeSIL.h"

#include "DeserializationErrors.h"
#include "SILFormat.h"

#include "swift/Basic/Defer.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/Serialization/ModuleFile.h"
#include "SILFormat.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILDebugScope.h"
Expand All @@ -38,6 +41,9 @@ using namespace swift::serialization;
using namespace swift::serialization::sil_block;
using namespace llvm::support;

const char SILEntityError::ID = '\0';
void SILEntityError::anchor() {}

STATISTIC(NumDeserializedFunc, "Number of deserialized SIL functions");

static Optional<StringLiteralInst::Encoding>
Expand Down Expand Up @@ -339,7 +345,14 @@ SILFunction *SILDeserializer::getFuncForReference(StringRef name,
// Otherwise, look for a function with this name in the module.
auto iter = FuncTable->find(name);
if (iter != FuncTable->end()) {
fn = readSILFunction(*iter, nullptr, name, /*declarationOnly*/ true);
auto maybeFn = readSILFunctionChecked(*iter, nullptr, name,
/*declarationOnly*/ true);
if (maybeFn) {
fn = maybeFn.get();
} else {
// Ignore the failure; we'll synthesize a bogus function instead.
llvm::consumeError(maybeFn.takeError());
}
}
}

Expand All @@ -362,7 +375,15 @@ SILFunction *SILDeserializer::getFuncForReference(StringRef name) {
if (iter == FuncTable->end())
return nullptr;

return readSILFunction(*iter, nullptr, name, /*declarationOnly*/ true);
auto maybeFn = readSILFunctionChecked(*iter, nullptr, name,
/*declarationOnly*/ true);
if (!maybeFn) {
// Ignore the failure and just pretend the function doesn't exist
llvm::consumeError(maybeFn.takeError());
return nullptr;
}

return maybeFn.get();
}

/// Helper function to find a SILGlobalVariable given its name. It first checks
Expand All @@ -386,6 +407,19 @@ SILFunction *SILDeserializer::readSILFunction(DeclID FID,
StringRef name,
bool declarationOnly,
bool errorIfEmptyBody) {
llvm::Expected<SILFunction *> deserialized =
readSILFunctionChecked(FID, existingFn, name, declarationOnly,
errorIfEmptyBody);
if (!deserialized) {
MF->fatal(deserialized.takeError());
}
return deserialized.get();
}

llvm::Expected<SILFunction *>
SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
StringRef name, bool declarationOnly,
bool errorIfEmptyBody) {
// We can't deserialize function bodies after IRGen lowering passes have
// happened since other definitions in the module will no longer be in
// canonical SIL form.
Expand Down Expand Up @@ -444,7 +478,16 @@ SILFunction *SILDeserializer::readSILFunction(DeclID FID,
MF->error();
return nullptr;
}
auto ty = getSILType(MF->getType(funcTyID), SILValueCategory::Object);
auto astType = MF->getTypeChecked(funcTyID);
if (!astType) {
if (!existingFn || errorIfEmptyBody) {
return llvm::make_error<SILEntityError>(
name, takeErrorInfo(astType.takeError()));
}
llvm::consumeError(astType.takeError());
return existingFn;
}
auto ty = getSILType(astType.get(), SILValueCategory::Object);
if (!ty.is<SILFunctionType>()) {
DEBUG(llvm::dbgs() << "not a function type for SILFunction\n");
MF->error();
Expand Down Expand Up @@ -2396,14 +2439,21 @@ SILFunction *SILDeserializer::lookupSILFunction(SILFunction *InFunc) {
if (iter == FuncTable->end())
return nullptr;

auto Func = readSILFunction(*iter, InFunc, name, /*declarationOnly*/ false);
if (Func) {
auto maybeFunc = readSILFunctionChecked(*iter, InFunc, name,
/*declarationOnly*/ false);
if (!maybeFunc) {
// Ignore the error; treat it as if we didn't have a definition.
llvm::consumeError(maybeFunc.takeError());
return nullptr;
}

if (maybeFunc.get()) {
DEBUG(llvm::dbgs() << "Deserialize SIL:\n";
Func->dump());
assert(InFunc->getName() == Func->getName());
maybeFunc.get()->dump());
assert(InFunc->getName() == maybeFunc.get()->getName());
}

return Func;
return maybeFunc.get();
}

/// Check for existence of a function with a given name and required linkage.
Expand Down Expand Up @@ -2480,11 +2530,20 @@ SILFunction *SILDeserializer::lookupSILFunction(StringRef name,
if (iter == FuncTable->end())
return nullptr;

auto Func = readSILFunction(*iter, nullptr, name, declarationOnly);
if (Func)
auto maybeFunc = readSILFunctionChecked(*iter, nullptr, name,
declarationOnly);

if (!maybeFunc) {
// Ignore the error; treat it as if we didn't have a definition.
llvm::consumeError(maybeFunc.takeError());
return nullptr;
}

if (maybeFunc.get()) {
DEBUG(llvm::dbgs() << "Deserialize SIL:\n";
Func->dump());
return Func;
maybeFunc.get()->dump());
}
return maybeFunc.get();
}

SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) {
Expand Down Expand Up @@ -2580,8 +2639,12 @@ void SILDeserializer::getAllSILFunctions() {
auto DI = FuncTable->find(*KI);
assert(DI != FuncTable->end() && "There should never be a key without data.");

readSILFunction(*DI, nullptr, *KI, false,
false/*errorIfEmptyBody*/);
auto maybeFunc = readSILFunctionChecked(*DI, nullptr, *KI, false,
false/*errorIfEmptyBody*/);
if (!maybeFunc) {
// Ignore the error; treat it as if we didn't have a definition.
llvm::consumeError(maybeFunc.takeError());
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions lib/Serialization/DeserializeSIL.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ namespace swift {
SILFunction *readSILFunction(serialization::DeclID, SILFunction *InFunc,
StringRef Name, bool declarationOnly,
bool errorIfEmptyBody = true);
/// Read a SIL function.
llvm::Expected<SILFunction *>
readSILFunctionChecked(serialization::DeclID, SILFunction *InFunc,
StringRef Name, bool declarationOnly,
bool errorIfEmptyBody = true);

/// Read a SIL basic block within a given SIL function.
SILBasicBlock *readSILBasicBlock(SILFunction *Fn,
SILBasicBlock *Prev,
Expand Down
3 changes: 3 additions & 0 deletions test/SIL/Serialization/Recovery/Inputs/bad-modules/Types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// struct SoonToBeMissing {
// int value;
// };
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Types { header "Types.h" }
3 changes: 3 additions & 0 deletions test/SIL/Serialization/Recovery/Inputs/good-modules/Types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
struct SoonToBeMissing {
int value;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Types { header "Types.h" }
26 changes: 26 additions & 0 deletions test/SIL/Serialization/Recovery/function.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -parse-sil %s -emit-sib -o %t/Library.sib -module-name Library -I %S/Inputs/good-modules -parse-stdlib
// RUN: %target-sil-opt %t/Library.sib -I %S/Inputs/good-modules | %FileCheck %s
// RUN: %target-sil-opt %t/Library.sib -I %S/Inputs/bad-modules | %FileCheck -check-prefix=CHECK-RECOVERY %s
// RUN: %target-sil-opt %t/Library.sib -I %S/Inputs/bad-modules | %FileCheck -check-prefix=CHECK-RECOVERY-NEGATIVE %s

// CHECK-LABEL: sil_stage raw
// CHECK-RECOVERY-LABEL: sil_stage raw

sil_stage raw
import Types

// CHECK-LABEL: sil @missingParam : $@convention(thin) (SoonToBeMissing) -> () {
// CHECK-RECOVERY-NEGATIVE-NOT: sil @missingParam
sil @missingParam : $@convention(thin) (SoonToBeMissing) -> () {
entry(%arg: $SoonToBeMissing):
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @missingResult : $@convention(thin) () -> SoonToBeMissing {
// CHECK-RECOVERY-NEGATIVE-NOT: sil @missingResult
sil @missingResult : $@convention(thin) () -> (SoonToBeMissing) {
entry:
unreachable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module rdar40899824Helper { header "rdar40899824Helper.h" }
Loading