Skip to content

Commit a364aed

Browse files
authored
Merge pull request #38779 from apple/serialization-crashes
Fix serialization crashes found from stress tester
2 parents f82a9b3 + 69a5a3d commit a364aed

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

lib/Serialization/Serialization.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,8 +2084,10 @@ void Serializer::writePatternBindingInitializer(PatternBindingDecl *binding,
20842084
StringRef initStr;
20852085
SmallString<128> scratch;
20862086
auto varDecl = binding->getAnchoringVarDecl(bindingIndex);
2087+
assert((varDecl || allowCompilerErrors()) &&
2088+
"Serializing PDB without anchoring VarDecl");
20872089
if (binding->hasInitStringRepresentation(bindingIndex) &&
2088-
varDecl->isInitExposedToClients()) {
2090+
varDecl && varDecl->isInitExposedToClients()) {
20892091
initStr = binding->getInitStringRepresentation(bindingIndex, scratch);
20902092
}
20912093

@@ -3957,9 +3959,6 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
39573959
using namespace decls_block;
39583960
verifyAttrSerializable(dtor);
39593961

3960-
if (S.allowCompilerErrors() && dtor->isInvalid())
3961-
return;
3962-
39633962
auto contextID = S.addDeclContextRef(dtor->getDeclContext());
39643963

39653964
unsigned abbrCode = S.DeclTypeAbbrCodes[DestructorLayout::Code];
@@ -4001,12 +4000,34 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
40014000
}
40024001
};
40034002

4003+
/// When allowing modules with errors there may be cases where there's little
4004+
/// point in serializing a declaration and doing so would create a maintenance
4005+
/// burden on the deserialization side. Returns \c true if the given declaration
4006+
/// should be skipped and \c false otherwise.
4007+
static bool canSkipWhenInvalid(const Decl *D) {
4008+
// There's no point writing out the deinit when its context is not a class
4009+
// as nothing would be able to reference it
4010+
if (auto *deinit = dyn_cast<DestructorDecl>(D)) {
4011+
if (!isa<ClassDecl>(D->getDeclContext()))
4012+
return true;
4013+
}
4014+
return false;
4015+
}
4016+
40044017
void Serializer::writeASTBlockEntity(const Decl *D) {
40054018
using namespace decls_block;
40064019

40074020
PrettyStackTraceDecl trace("serializing", D);
40084021
assert(DeclsToSerialize.hasRef(D));
40094022

4023+
if (D->isInvalid()) {
4024+
assert(allowCompilerErrors() &&
4025+
"cannot create a module with an invalid decl");
4026+
4027+
if (canSkipWhenInvalid(D))
4028+
return;
4029+
}
4030+
40104031
BitOffset initialOffset = Out.GetCurrentBitNo();
40114032
SWIFT_DEFER {
40124033
// This is important enough to leave on in Release builds.
@@ -4016,8 +4037,6 @@ void Serializer::writeASTBlockEntity(const Decl *D) {
40164037
}
40174038
};
40184039

4019-
assert((allowCompilerErrors() || !D->isInvalid()) &&
4020-
"cannot create a module with an invalid decl");
40214040
if (isDeclXRef(D)) {
40224041
writeCrossReference(D);
40234042
return;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// Serialize and deserialize a deinit with SourceFile context to make sure we
4+
// don't crash
5+
// RUN: %target-swift-frontend -verify -module-name errors -emit-module -o %t/errors.swiftmodule -experimental-allow-module-with-compiler-errors %s
6+
// RUN: %target-swift-ide-test -print-module -module-to-print=errors -source-filename=x -I %t -allow-compiler-errors
7+
8+
// Also check it wasn't serialized
9+
// RUN: llvm-bcanalyzer -dump %t/errors.swiftmodule | %FileCheck %s
10+
// CHECK-NOT: DESTRUCTOR_DECL
11+
12+
struct Foo {}
13+
14+
@discardableResult // expected-error{{'@discardableResult' attribute cannot be applied to this declaration}}
15+
deinit {} // expected-error{{deinitializers may only be declared within a class}}
16+
17+
func foo() -> Foo { return Foo() }
18+
19+
// Make sure @discardableResult isn't added to `foo`, which could be possible
20+
// if the deinit is partially serialized
21+
foo() // expected-warning{{result of call to 'foo()' is unused}}

validation-test/Serialization/AllowErrors/invalid-deinit.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// -parse-as-library added so that the PDB isn't added to a TopLevelCodeDecl,
4+
// which isn't serialized at all
5+
// RUN: %target-swift-frontend -emit-module -o %t/errors.swiftmodule -module-name errors -experimental-allow-module-with-compiler-errors -parse-as-library %s
6+
7+
let self = 1

0 commit comments

Comments
 (0)