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';
|
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
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
@@ -1220,9 +1240,23 @@ bool ModuleFile::readMembers(SmallVectorImpl<Decl *> &Members) {
|
|||||||
|
|
||||||
Members.reserve(rawMemberIDs.size());
|
Members.reserve(rawMemberIDs.size());
|
||||||
for (DeclID rawID : rawMemberIDs) {
|
for (DeclID rawID : rawMemberIDs) {
|
||||||
Decl *D = getDecl(rawID);
|
Expected<Decl *> D = getDeclChecked(rawID);
|
||||||
assert(D && "unable to deserialize next member");
|
if (!D) {
|
||||||
Members.push_back(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;
|
return false;
|
||||||
@@ -2916,12 +2950,31 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
|||||||
overriddenID, accessorStorageDeclID,
|
overriddenID, accessorStorageDeclID,
|
||||||
hasCompoundName, rawAddressorKind,
|
hasCompoundName, rawAddressorKind,
|
||||||
rawAccessLevel, nameIDs);
|
rawAccessLevel, nameIDs);
|
||||||
|
|
||||||
// Resolve the name ids.
|
// Resolve the name ids.
|
||||||
SmallVector<Identifier, 2> names;
|
SmallVector<Identifier, 2> names;
|
||||||
for (auto nameID : nameIDs)
|
for (auto nameID : nameIDs)
|
||||||
names.push_back(getIdentifier(nameID));
|
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);
|
auto DC = getDeclContext(contextID);
|
||||||
if (declOrOffset.isComplete())
|
if (declOrOffset.isComplete())
|
||||||
return declOrOffset;
|
return declOrOffset;
|
||||||
@@ -2940,14 +2993,6 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
|||||||
if (declOrOffset.isComplete())
|
if (declOrOffset.isComplete())
|
||||||
return declOrOffset;
|
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(
|
auto fn = FuncDecl::createDeserialized(
|
||||||
ctx, /*StaticLoc=*/SourceLoc(), staticSpelling.getValue(),
|
ctx, /*StaticLoc=*/SourceLoc(), staticSpelling.getValue(),
|
||||||
/*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(),
|
/*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(),
|
||||||
@@ -3004,8 +3049,8 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
|||||||
if (auto errorConvention = maybeReadForeignErrorConvention())
|
if (auto errorConvention = maybeReadForeignErrorConvention())
|
||||||
fn->setForeignErrorConvention(*errorConvention);
|
fn->setForeignErrorConvention(*errorConvention);
|
||||||
|
|
||||||
if (auto overridden = cast_or_null<FuncDecl>(getDecl(overriddenID))) {
|
if (auto overriddenFunc = cast_or_null<FuncDecl>(overridden.get())) {
|
||||||
fn->setOverriddenDecl(overridden);
|
fn->setOverriddenDecl(overriddenFunc);
|
||||||
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
|
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