Skip to content

Commit 73d4526

Browse files
committed
[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.
1 parent 6fdb076 commit 73d4526

File tree

4 files changed

+92
-14
lines changed

4 files changed

+92
-14
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,26 @@ namespace {
297297
}
298298
};
299299
const char XRefError::ID = '\0';
300+
301+
class OverrideError : public llvm::ErrorInfo<OverrideError> {
302+
friend ErrorInfo;
303+
static const char ID;
304+
305+
DeclName name;
306+
public:
307+
explicit OverrideError(DeclName name) : name(name) {}
308+
309+
void log(raw_ostream &OS) const override {
310+
OS << "could not find '" << name << "' in parent class";
311+
}
312+
313+
std::error_code convertToErrorCode() const override {
314+
// This is a deprecated part of llvm::Error, so we just return a very
315+
// generic value.
316+
return {EINVAL, std::generic_category()};
317+
}
318+
};
319+
const char OverrideError::ID = '\0';
300320
} // end anonymous namespace
301321

302322

@@ -1220,9 +1240,23 @@ bool ModuleFile::readMembers(SmallVectorImpl<Decl *> &Members) {
12201240

12211241
Members.reserve(rawMemberIDs.size());
12221242
for (DeclID rawID : rawMemberIDs) {
1223-
Decl *D = getDecl(rawID);
1224-
assert(D && "unable to deserialize next member");
1225-
Members.push_back(D);
1243+
Expected<Decl *> D = getDeclChecked(rawID);
1244+
if (!D) {
1245+
if (!getContext().LangOpts.EnableDeserializationRecovery)
1246+
fatal(D.takeError());
1247+
1248+
// Silently drop the member if there was a problem.
1249+
// FIXME: This isn't sound for protocols; we need to at least record that
1250+
// it happened.
1251+
llvm::handleAllErrors(D.takeError(),
1252+
[](const OverrideError &) { /* expected */ },
1253+
[&](std::unique_ptr<llvm::ErrorInfoBase> unhandled){
1254+
fatal(std::move(unhandled));
1255+
});
1256+
continue;
1257+
}
1258+
assert(D.get() && "unchecked error deserializing next member");
1259+
Members.push_back(D.get());
12261260
}
12271261

12281262
return false;
@@ -2916,12 +2950,31 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
29162950
overriddenID, accessorStorageDeclID,
29172951
hasCompoundName, rawAddressorKind,
29182952
rawAccessLevel, nameIDs);
2919-
2953+
29202954
// Resolve the name ids.
29212955
SmallVector<Identifier, 2> names;
29222956
for (auto nameID : nameIDs)
29232957
names.push_back(getIdentifier(nameID));
29242958

2959+
DeclName name;
2960+
if (!names.empty()) {
2961+
if (hasCompoundName)
2962+
name = DeclName(ctx, names[0],
2963+
llvm::makeArrayRef(names.begin() + 1, names.end()));
2964+
else
2965+
name = DeclName(names[0]);
2966+
}
2967+
2968+
Expected<Decl *> overridden = getDeclChecked(overriddenID);
2969+
if (!overridden) {
2970+
llvm::handleAllErrors(overridden.takeError(),
2971+
[](const XRefError &) { /* expected */ },
2972+
[&](std::unique_ptr<llvm::ErrorInfoBase> unhandled){
2973+
fatal(std::move(unhandled));
2974+
});
2975+
return llvm::make_error<OverrideError>(name);
2976+
}
2977+
29252978
auto DC = getDeclContext(contextID);
29262979
if (declOrOffset.isComplete())
29272980
return declOrOffset;
@@ -2940,14 +2993,6 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
29402993
if (declOrOffset.isComplete())
29412994
return declOrOffset;
29422995

2943-
DeclName name;
2944-
if (!names.empty()) {
2945-
if (hasCompoundName)
2946-
name = DeclName(ctx, names[0],
2947-
llvm::makeArrayRef(names.begin() + 1, names.end()));
2948-
else
2949-
name = DeclName(names[0]);
2950-
}
29512996
auto fn = FuncDecl::createDeserialized(
29522997
ctx, /*StaticLoc=*/SourceLoc(), staticSpelling.getValue(),
29532998
/*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(),
@@ -3004,8 +3049,8 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
30043049
if (auto errorConvention = maybeReadForeignErrorConvention())
30053050
fn->setForeignErrorConvention(*errorConvention);
30063051

3007-
if (auto overridden = cast_or_null<FuncDecl>(getDecl(overriddenID))) {
3008-
fn->setOverriddenDecl(overridden);
3052+
if (auto overriddenFunc = cast_or_null<FuncDecl>(overridden.get())) {
3053+
fn->setOverriddenDecl(overriddenFunc);
30093054
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
30103055
}
30113056

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@interface Base
2+
#ifndef BAD
3+
- (void)method;
4+
#endif
5+
@end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module Overrides { header "Overrides.h" }
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-swift-frontend -emit-module -o %t -module-name Lib -I %S/Inputs/custom-modules %s
3+
4+
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s
5+
6+
// 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
7+
// 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
8+
9+
// REQUIRES: objc_interop
10+
11+
import Overrides
12+
13+
public class Sub: Base {
14+
public override func method() {}
15+
}
16+
17+
// CHECK-LABEL: class Sub : Base {
18+
// CHECK-NEXT: func method()
19+
// CHECK-NEXT: {{^}$}}
20+
21+
// CHECK-CRASH: error: fatal error encountered while reading from module 'Lib'; please file a bug report with your project and the crash log
22+
// CHECK-CRASH-LABEL: *** DESERIALIZATION FAILURE (please include this section in any bug report) ***
23+
// CHECK-CRASH: could not find 'method()' in parent class
24+
// CHECK-CRASH: While loading members for 'Sub' in module 'Lib'
25+
26+
// CHECK-RECOVERY-LABEL: class Sub : Base {
27+
// CHECK-RECOVERY-NEXT: {{^}$}}

0 commit comments

Comments
 (0)