[Serialization] Drop decls whose types can't be deserialized.

Proof-of-concept for the above. This shouldn't be common---renames are
far more likely, and those we can track---but occurs when the
swift_wrapper attribute (the implementation of NS_STRING_ENUM) is
active in Swift 4 but not in Swift 3.

Note that this only checks the canonical interface type of the
declaration, because the non-canonical type may contain references to
the declaration's generic parameters.
This commit is contained in:
Jordan Rose
2017-04-26 17:47:19 -07:00
parent 10c62545c8
commit 1168cacf4f
7 changed files with 158 additions and 50 deletions

View File

@@ -44,7 +44,7 @@ const unsigned char MODULE_DOC_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x07 };
/// Serialized module format major version number.
///
/// Always 0 for Swift 1.x - 3.x.
/// Always 0 for Swift 1.x - 4.x.
const uint16_t VERSION_MAJOR = 0;
/// Serialized module format minor version number.
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
/// in source control, you should also update the comment to briefly
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
const uint16_t VERSION_MINOR = 338; // Last change: OPERAND_WITH_ATTR format.
const uint16_t VERSION_MINOR = 339; // Last change: member canonical types
using DeclID = PointerEmbeddedInt<unsigned, 31>;
using DeclIDField = BCFixed<31>;
@@ -858,7 +858,8 @@ namespace decls_block {
BCFixed<1>, // throws?
CtorInitializerKindField, // initializer kind
GenericEnvironmentIDField, // generic environment
TypeIDField, // type (interface)
TypeIDField, // interface type
TypeIDField, // canonical interface type
DeclIDField, // overridden decl
AccessibilityKindField, // accessibility
BCArray<IdentifierIDField> // argument names
@@ -877,6 +878,7 @@ namespace decls_block {
BCFixed<1>, // HasNonPatternBindingInit?
StorageKindField, // StorageKind
TypeIDField, // interface type
TypeIDField, // canonical interface type
DeclIDField, // getter
DeclIDField, // setter
DeclIDField, // materializeForSet
@@ -911,6 +913,7 @@ namespace decls_block {
BCVBR<5>, // number of parameter patterns
GenericEnvironmentIDField, // generic environment
TypeIDField, // interface type
TypeIDField, // canonical interface type
DeclIDField, // operator decl
DeclIDField, // overridden function
DeclIDField, // AccessorStorageDecl
@@ -982,6 +985,7 @@ namespace decls_block {
StorageKindField, // StorageKind
GenericEnvironmentIDField, // generic environment
TypeIDField, // interface type
TypeIDField, // canonical interface type
DeclIDField, // getter
DeclIDField, // setter
DeclIDField, // materializeForSet

View File

@@ -127,6 +127,18 @@ const char XRefError::ID = '\0';
void XRefError::anchor() {}
const char OverrideError::ID = '\0';
void OverrideError::anchor() {}
const char TypeError::ID = '\0';
void TypeError::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.
@@ -2492,7 +2504,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
bool isImplicit, isObjC, hasStubImplementation, throws;
GenericEnvironmentID genericEnvID;
uint8_t storedInitKind, rawAccessLevel;
TypeID interfaceID;
TypeID interfaceID, canonicalTypeID;
DeclID overriddenID;
ArrayRef<uint64_t> argNameIDs;
@@ -2501,8 +2513,8 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
isObjC, hasStubImplementation,
throws, storedInitKind,
genericEnvID, interfaceID,
overriddenID, rawAccessLevel,
argNameIDs);
canonicalTypeID, overriddenID,
rawAccessLevel, argNameIDs);
// Resolve the name ids.
SmallVector<Identifier, 2> argNames;
@@ -2595,15 +2607,15 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
DeclContextID contextID;
bool isImplicit, isObjC, isStatic, isLet, hasNonPatternBindingInit;
uint8_t storageKind, rawAccessLevel, rawSetterAccessLevel;
TypeID interfaceTypeID;
TypeID interfaceTypeID, canonicalTypeID;
DeclID getterID, setterID, materializeForSetID, willSetID, didSetID;
DeclID addressorID, mutableAddressorID, overriddenID;
decls_block::VarLayout::readRecord(scratch, nameID, contextID,
isImplicit, isObjC, isStatic, isLet,
hasNonPatternBindingInit, storageKind,
interfaceTypeID, getterID,
setterID, materializeForSetID,
interfaceTypeID, canonicalTypeID,
getterID, setterID, materializeForSetID,
addressorID, mutableAddressorID,
willSetID, didSetID, overriddenID,
rawAccessLevel, rawSetterAccessLevel);
@@ -2616,6 +2628,10 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
return llvm::make_error<OverrideError>(name);
}
auto canonicalType = getTypeChecked(canonicalTypeID);
if (!canonicalType)
return llvm::make_error<TypeError>(name, takeErrorInfo(canonicalType.takeError()));
auto DC = ForcedContext ? *ForcedContext : getDeclContext(contextID);
if (declOrOffset.isComplete())
return declOrOffset;
@@ -2703,7 +2719,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
bool isObjC, isMutating, hasDynamicSelf, throws;
unsigned numParamPatterns;
GenericEnvironmentID genericEnvID;
TypeID interfaceTypeID;
TypeID interfaceTypeID, canonicalTypeID;
DeclID associatedDeclID;
DeclID overriddenID;
DeclID accessorStorageDeclID;
@@ -2714,10 +2730,11 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
isStatic, rawStaticSpelling, isObjC,
isMutating, hasDynamicSelf, throws,
numParamPatterns, genericEnvID,
interfaceTypeID, associatedDeclID,
overriddenID, accessorStorageDeclID,
hasCompoundName, rawAddressorKind,
rawAccessLevel, nameIDs);
interfaceTypeID, canonicalTypeID,
associatedDeclID, overriddenID,
accessorStorageDeclID, hasCompoundName,
rawAddressorKind, rawAccessLevel,
nameIDs);
// Resolve the name ids.
SmallVector<Identifier, 2> names;
@@ -3237,7 +3254,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
DeclContextID contextID;
bool isImplicit, isObjC;
GenericEnvironmentID genericEnvID;
TypeID interfaceTypeID;
TypeID interfaceTypeID, canonicalTypeID;
DeclID getterID, setterID, materializeForSetID;
DeclID addressorID, mutableAddressorID, willSetID, didSetID;
DeclID overriddenID;
@@ -3248,7 +3265,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
decls_block::SubscriptLayout::readRecord(scratch, contextID,
isImplicit, isObjC, rawStorageKind,
genericEnvID,
interfaceTypeID,
interfaceTypeID, canonicalTypeID,
getterID, setterID,
materializeForSetID,
addressorID, mutableAddressorID,
@@ -3587,17 +3604,20 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
TypeID canonicalTypeID;
decls_block::NameAliasTypeLayout::readRecord(scratch, underlyingID,
canonicalTypeID);
auto alias = dyn_cast_or_null<TypeAliasDecl>(getDecl(underlyingID));
if (!alias) {
error();
return nullptr;
}
auto aliasOrError = getDeclChecked(underlyingID);
if (!aliasOrError)
return aliasOrError.takeError();
auto alias = dyn_cast<TypeAliasDecl>(aliasOrError.get());
if (ctx.LangOpts.EnableDeserializationRecovery) {
if (Type expectedType = getType(canonicalTypeID)) {
if (!alias->getDeclaredInterfaceType()->isEqual(expectedType)) {
Expected<Type> expectedType = getTypeChecked(canonicalTypeID);
if (!expectedType)
return expectedType.takeError();
if (expectedType.get()) {
if (!alias ||
!alias->getDeclaredInterfaceType()->isEqual(expectedType.get())) {
// Fall back to the canonical type.
typeOrOffset = expectedType;
typeOrOffset = expectedType.get();
break;
}
}
@@ -3619,12 +3639,22 @@ Expected<Type> ModuleFile::getTypeChecked(TypeID TID) {
TypeID parentID;
decls_block::NominalTypeLayout::readRecord(scratch, declID, parentID);
Type parentTy = getType(parentID);
Expected<Type> parentTy = getTypeChecked(parentID);
if (!parentTy)
return parentTy.takeError();
// Record the type as soon as possible. Members of a nominal type often
// try to refer back to the type.
auto nominal = cast<NominalTypeDecl>(getDecl(declID));
typeOrOffset = NominalType::get(nominal, parentTy, ctx);
auto nominalOrError = getDeclChecked(declID);
if (!nominalOrError)
return nominalOrError.takeError();
auto nominal = dyn_cast<NominalTypeDecl>(nominalOrError.get());
if (!nominal) {
XRefTracePath tinyTrace{*nominalOrError.get()->getModuleContext()};
tinyTrace.addValue(cast<ValueDecl>(nominalOrError.get())->getName());
return llvm::make_error<XRefError>("declaration is not a nominal type",
tinyTrace);
}
typeOrOffset = NominalType::get(nominal, parentTy.get(), ctx);
assert(typeOrOffset.isComplete());
break;

View File

@@ -248,6 +248,30 @@ public:
}
};
class TypeError : public llvm::ErrorInfo<TypeError, DeclDeserializationError> {
friend ErrorInfo;
static const char ID;
void anchor() override;
DeclName name;
std::unique_ptr<ErrorInfoBase> underlyingReason;
public:
explicit TypeError(DeclName name, std::unique_ptr<ErrorInfoBase> reason)
: name(name), underlyingReason(std::move(reason)) {}
void log(raw_ostream &OS) const override {
OS << "could not deserialize type for '" << name << "'";
if (underlyingReason) {
OS << ": ";
underlyingReason->log(OS);
}
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};
} // end namespace serialization
} // end namespace swift

View File

@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "swift/Serialization/ModuleFile.h"
#include "DeserializationErrors.h"
#include "swift/Serialization/ModuleFormat.h"
#include "swift/Subsystems.h"
#include "swift/AST/ASTContext.h"
@@ -31,6 +32,7 @@
using namespace swift;
using namespace swift::serialization;
using namespace llvm::support;
using llvm::Expected;
static bool checkModuleSignature(llvm::BitstreamCursor &cursor) {
for (unsigned char byte : MODULE_SIGNATURE)
@@ -1310,20 +1312,20 @@ void ModuleFile::lookupValue(DeclName name,
// serialized.
auto iter = TopLevelDecls->find(name.getBaseName());
if (iter != TopLevelDecls->end()) {
if (name.isSimpleName()) {
for (auto item : *iter) {
auto VD = cast<ValueDecl>(getDecl(item.second));
results.push_back(VD);
Expected<Decl *> declOrError = getDeclChecked(item.second);
if (!declOrError) {
if (!getContext().LangOpts.EnableDeserializationRecovery)
fatal(declOrError.takeError());
llvm::consumeError(declOrError.takeError());
continue;
}
} else {
for (auto item : *iter) {
auto VD = cast<ValueDecl>(getDecl(item.second));
if (VD->getFullName().matchesRef(name))
auto VD = cast<ValueDecl>(declOrError.get());
if (name.isSimpleName() || VD->getFullName().matchesRef(name))
results.push_back(VD);
}
}
}
}
// If the name is an operator name, also look for operator methods.
if (name.isOperator() && OperatorMethodDecls) {
@@ -1503,21 +1505,32 @@ void ModuleFile::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath,
if (!TopLevelDecls)
return;
auto tryImport = [this, &consumer](DeclID ID) {
Expected<Decl *> declOrError = getDeclChecked(ID);
if (!declOrError) {
if (!getContext().LangOpts.EnableDeserializationRecovery)
fatal(declOrError.takeError());
llvm::consumeError(declOrError.takeError());
return;
}
consumer.foundDecl(cast<ValueDecl>(declOrError.get()),
DeclVisibilityKind::VisibleAtTopLevel);
};
if (!accessPath.empty()) {
auto iter = TopLevelDecls->find(accessPath.front().first);
if (iter == TopLevelDecls->end())
return;
for (auto item : *iter)
consumer.foundDecl(cast<ValueDecl>(getDecl(item.second)),
DeclVisibilityKind::VisibleAtTopLevel);
tryImport(item.second);
return;
}
for (auto entry : TopLevelDecls->data()) {
for (auto item : entry)
consumer.foundDecl(cast<ValueDecl>(getDecl(item.second)),
DeclVisibilityKind::VisibleAtTopLevel);
tryImport(item.second);
}
}
@@ -1711,8 +1724,16 @@ void ModuleFile::getTopLevelDecls(SmallVectorImpl<Decl *> &results) {
if (TopLevelDecls) {
for (auto entry : TopLevelDecls->data()) {
for (auto item : entry)
results.push_back(getDecl(item.second));
for (auto item : entry) {
Expected<Decl *> declOrError = getDeclChecked(item.second);
if (!declOrError) {
if (!getContext().LangOpts.EnableDeserializationRecovery)
fatal(declOrError.takeError());
llvm::consumeError(declOrError.takeError());
continue;
}
results.push_back(declOrError.get());
}
}
}

View File

@@ -2761,6 +2761,7 @@ void Serializer::writeDecl(const Decl *D) {
if (var->isSettable(nullptr))
rawSetterAccessLevel =
getRawStableAccessibility(var->getSetterAccessibility());
Type ty = var->getInterfaceType();
unsigned abbrCode = DeclTypeAbbrCodes[VarLayout::Code];
VarLayout::emitRecord(Out, ScratchRecord, abbrCode,
@@ -2772,7 +2773,8 @@ void Serializer::writeDecl(const Decl *D) {
var->isLet(),
var->hasNonPatternBindingInit(),
(unsigned) accessors.Kind,
addTypeRef(var->getInterfaceType()),
addTypeRef(ty),
addTypeRef(ty->getCanonicalType()),
addDeclRef(accessors.Get),
addDeclRef(accessors.Set),
addDeclRef(accessors.MaterializeForSet),
@@ -2824,6 +2826,7 @@ void Serializer::writeDecl(const Decl *D) {
getRawStableAccessibility(fn->getFormalAccess());
uint8_t rawAddressorKind =
getRawStableAddressorKind(fn->getAddressorKind());
Type ty = fn->getInterfaceType();
FuncLayout::emitRecord(Out, ScratchRecord, abbrCode,
contextID,
@@ -2838,7 +2841,8 @@ void Serializer::writeDecl(const Decl *D) {
fn->getParameterLists().size(),
addGenericEnvironmentRef(
fn->getGenericEnvironment()),
addTypeRef(fn->getInterfaceType()),
addTypeRef(ty),
addTypeRef(ty->getCanonicalType()),
addDeclRef(fn->getOperatorDecl()),
addDeclRef(fn->getOverriddenDecl()),
addDeclRef(fn->getAccessorStorageDecl()),
@@ -2906,6 +2910,7 @@ void Serializer::writeDecl(const Decl *D) {
if (subscript->isSettable())
rawSetterAccessLevel =
getRawStableAccessibility(subscript->getSetterAccessibility());
Type ty = subscript->getInterfaceType();
unsigned abbrCode = DeclTypeAbbrCodes[SubscriptLayout::Code];
SubscriptLayout::emitRecord(Out, ScratchRecord, abbrCode,
@@ -2915,7 +2920,8 @@ void Serializer::writeDecl(const Decl *D) {
(unsigned) accessors.Kind,
addGenericEnvironmentRef(
subscript->getGenericEnvironment()),
addTypeRef(subscript->getInterfaceType()),
addTypeRef(ty),
addTypeRef(ty->getCanonicalType()),
addDeclRef(accessors.Get),
addDeclRef(accessors.Set),
addDeclRef(accessors.MaterializeForSet),
@@ -2946,6 +2952,7 @@ void Serializer::writeDecl(const Decl *D) {
uint8_t rawAccessLevel =
getRawStableAccessibility(ctor->getFormalAccess());
Type ty = ctor->getInterfaceType();
unsigned abbrCode = DeclTypeAbbrCodes[ConstructorLayout::Code];
ConstructorLayout::emitRecord(Out, ScratchRecord, abbrCode,
@@ -2960,7 +2967,8 @@ void Serializer::writeDecl(const Decl *D) {
ctor->getInitKind()),
addGenericEnvironmentRef(
ctor->getGenericEnvironment()),
addTypeRef(ctor->getInterfaceType()),
addTypeRef(ty),
addTypeRef(ty->getCanonicalType()),
addDeclRef(ctor->getOverriddenDecl()),
rawAccessLevel,
nameComponents);

View File

@@ -9,3 +9,11 @@ struct ImportedType {
};
typedef MysteryTypedef ImportedTypeAssoc __attribute__((swift_name("ImportedType.Assoc")));
#if !BAD
typedef int WrappedInt __attribute__((swift_wrapper(struct)));
typedef int UnwrappedInt;
#else
typedef int WrappedInt;
typedef int UnwrappedInt __attribute__((swift_wrapper(struct)));
#endif

View File

@@ -3,7 +3,9 @@
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -enable-experimental-deserialization-recovery | %FileCheck -check-prefix CHECK-RECOVERY %s
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -enable-experimental-deserialization-recovery > %t.txt
// RUN: %FileCheck -check-prefix CHECK-RECOVERY %s < %t.txt
// RUN: %FileCheck -check-prefix CHECK-RECOVERY-NEGATIVE %s < %t.txt
// RUN: %target-swift-frontend -typecheck -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery -DVERIFY %s -verify
// RUN: %target-swift-frontend -emit-silgen -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery %s | %FileCheck -check-prefix CHECK-SIL %s
@@ -30,6 +32,9 @@ let _: Int32? = useAssoc(ImportedType.self)
let _: String = useAssoc(AnotherType.self) // expected-error {{cannot convert call result type '_.Assoc?' to expected type 'String'}}
let _: Bool? = useAssoc(AnotherType.self) // expected-error {{cannot convert value of type 'AnotherType.Assoc?' (aka 'Optional<Int32>') to specified type 'Bool?'}}
let _: Int32? = useAssoc(AnotherType.self)
let _ = wrapped // expected-error {{use of unresolved identifier 'wrapped'}}
let _ = unwrapped // okay
#endif // VERIFY
#else // TEST
@@ -59,4 +64,12 @@ public let usesAssoc = useAssoc(ImportedType.self)
// CHECK-RECOVERY-DAG: let usesAssoc2: AnotherType.Assoc?
public let usesAssoc2 = useAssoc(AnotherType.self)
// CHECK-DAG: let wrapped: WrappedInt
// CHECK-RECOVERY-NEGATIVE-NOT: let wrapped:
public let wrapped = WrappedInt(0)
// CHECK-DAG: let unwrapped: UnwrappedInt
// CHECK-RECOVERY-DAG: let unwrapped: Int32
public let unwrapped: UnwrappedInt = 0
#endif // TEST