mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Serialization] Drop overriding methods if the base is missing.
Proof-of-concept for this sort of recovery. In the real world, it's more likely that this will happen due to differences between Swift 3 and Swift 4, rather than changes in what macros are defined, but the latter can still happen when debugging. There's a lot to do here to consider this production-ready. There are no generics involved and no potential circular references, and the /rest/ of the compiler isn't prepared for this either. But it's cool to see it working! Actually recovering is hidden behind the new -enable-experimental-deserialization-recovery option; without it the compiler will continue to eagerly abort.
This commit is contained in:
@@ -297,6 +297,26 @@ namespace {
|
||||
}
|
||||
};
|
||||
const char XRefError::ID = '\0';
|
||||
|
||||
class OverrideError : public llvm::ErrorInfo<OverrideError> {
|
||||
friend ErrorInfo;
|
||||
static const char ID;
|
||||
|
||||
DeclName name;
|
||||
public:
|
||||
explicit OverrideError(DeclName name) : name(name) {}
|
||||
|
||||
void log(raw_ostream &OS) const override {
|
||||
OS << "could not find '" << name << "' in parent class";
|
||||
}
|
||||
|
||||
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 OverrideError::ID = '\0';
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
@@ -1220,9 +1240,23 @@ bool ModuleFile::readMembers(SmallVectorImpl<Decl *> &Members) {
|
||||
|
||||
Members.reserve(rawMemberIDs.size());
|
||||
for (DeclID rawID : rawMemberIDs) {
|
||||
Decl *D = getDecl(rawID);
|
||||
assert(D && "unable to deserialize next member");
|
||||
Members.push_back(D);
|
||||
Expected<Decl *> D = getDeclChecked(rawID);
|
||||
if (!D) {
|
||||
if (!getContext().LangOpts.EnableDeserializationRecovery)
|
||||
fatal(D.takeError());
|
||||
|
||||
// Silently drop the member if there was a problem.
|
||||
// FIXME: This isn't sound for protocols; we need to at least record that
|
||||
// it happened.
|
||||
llvm::handleAllErrors(D.takeError(),
|
||||
[](const OverrideError &) { /* expected */ },
|
||||
[&](std::unique_ptr<llvm::ErrorInfoBase> unhandled){
|
||||
fatal(std::move(unhandled));
|
||||
});
|
||||
continue;
|
||||
}
|
||||
assert(D.get() && "unchecked error deserializing next member");
|
||||
Members.push_back(D.get());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -2916,12 +2950,31 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
overriddenID, accessorStorageDeclID,
|
||||
hasCompoundName, rawAddressorKind,
|
||||
rawAccessLevel, nameIDs);
|
||||
|
||||
|
||||
// Resolve the name ids.
|
||||
SmallVector<Identifier, 2> names;
|
||||
for (auto nameID : nameIDs)
|
||||
names.push_back(getIdentifier(nameID));
|
||||
|
||||
DeclName name;
|
||||
if (!names.empty()) {
|
||||
if (hasCompoundName)
|
||||
name = DeclName(ctx, names[0],
|
||||
llvm::makeArrayRef(names.begin() + 1, names.end()));
|
||||
else
|
||||
name = DeclName(names[0]);
|
||||
}
|
||||
|
||||
Expected<Decl *> overridden = getDeclChecked(overriddenID);
|
||||
if (!overridden) {
|
||||
llvm::handleAllErrors(overridden.takeError(),
|
||||
[](const XRefError &) { /* expected */ },
|
||||
[&](std::unique_ptr<llvm::ErrorInfoBase> unhandled){
|
||||
fatal(std::move(unhandled));
|
||||
});
|
||||
return llvm::make_error<OverrideError>(name);
|
||||
}
|
||||
|
||||
auto DC = getDeclContext(contextID);
|
||||
if (declOrOffset.isComplete())
|
||||
return declOrOffset;
|
||||
@@ -2940,14 +2993,6 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
if (declOrOffset.isComplete())
|
||||
return declOrOffset;
|
||||
|
||||
DeclName name;
|
||||
if (!names.empty()) {
|
||||
if (hasCompoundName)
|
||||
name = DeclName(ctx, names[0],
|
||||
llvm::makeArrayRef(names.begin() + 1, names.end()));
|
||||
else
|
||||
name = DeclName(names[0]);
|
||||
}
|
||||
auto fn = FuncDecl::createDeserialized(
|
||||
ctx, /*StaticLoc=*/SourceLoc(), staticSpelling.getValue(),
|
||||
/*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(),
|
||||
@@ -3004,8 +3049,8 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
if (auto errorConvention = maybeReadForeignErrorConvention())
|
||||
fn->setForeignErrorConvention(*errorConvention);
|
||||
|
||||
if (auto overridden = cast_or_null<FuncDecl>(getDecl(overriddenID))) {
|
||||
fn->setOverriddenDecl(overridden);
|
||||
if (auto overriddenFunc = cast_or_null<FuncDecl>(overridden.get())) {
|
||||
fn->setOverriddenDecl(overriddenFunc);
|
||||
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@interface Base
|
||||
#ifndef BAD
|
||||
- (void)method;
|
||||
#endif
|
||||
@end
|
||||
@@ -0,0 +1 @@
|
||||
module Overrides { header "Overrides.h" }
|
||||
27
test/Serialization/Recovery/overrides.swift
Normal file
27
test/Serialization/Recovery/overrides.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules %s
|
||||
|
||||
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s
|
||||
|
||||
// RUN: not --crash %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD 2>&1 | %FileCheck -check-prefix CHECK-CRASH %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
|
||||
|
||||
// REQUIRES: objc_interop
|
||||
|
||||
import Overrides
|
||||
|
||||
public class Sub: Base {
|
||||
public override func method() {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: class Sub : Base {
|
||||
// CHECK-NEXT: func method()
|
||||
// CHECK-NEXT: {{^}$}}
|
||||
|
||||
// CHECK-CRASH: error: fatal error encountered while reading from module 'Lib'; please file a bug report with your project and the crash log
|
||||
// CHECK-CRASH-LABEL: *** DESERIALIZATION FAILURE (please include this section in any bug report) ***
|
||||
// CHECK-CRASH: could not find 'method()' in parent class
|
||||
// CHECK-CRASH: While loading members for 'Sub' in module 'Lib'
|
||||
|
||||
// CHECK-RECOVERY-LABEL: class Sub : Base {
|
||||
// CHECK-RECOVERY-NEXT: {{^}$}}
|
||||
Reference in New Issue
Block a user