Skip to content

Commit ff7c6f6

Browse files
committed
[Serialization] Drop a class if the superclass can't be found
...instead of crashing. Also drop the class if its generic requirements depend on a type that can't be loaded (instead of crashing). rdar://problem/50125674
1 parent 64ce7b9 commit ff7c6f6

File tree

11 files changed

+202
-22
lines changed

11 files changed

+202
-22
lines changed

include/swift/Serialization/ModuleFormat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 491; // mangled class names as vtable keys
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 492; // dependency types for classes
5656

5757
using DeclIDField = BCFixed<31>;
5858

@@ -981,7 +981,8 @@ namespace decls_block {
981981
TypeIDField, // superclass
982982
AccessLevelField, // access level
983983
BCVBR<4>, // number of conformances
984-
BCArray<TypeIDField> // inherited types
984+
BCVBR<4>, // number of inherited types
985+
BCArray<TypeIDField> // inherited types, followed by dependency types
985986
// Trailed by the generic parameters (if any), the members record, and
986987
// finally conformance info (if any).
987988
>;

lib/Serialization/Deserialization.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,15 +3418,27 @@ class swift::DeclDeserializer {
34183418
GenericEnvironmentID genericEnvID;
34193419
TypeID superclassID;
34203420
uint8_t rawAccessLevel;
3421-
unsigned numConformances;
3422-
ArrayRef<uint64_t> rawInheritedIDs;
3421+
unsigned numConformances, numInheritedTypes;
3422+
ArrayRef<uint64_t> rawInheritedAndDependencyIDs;
34233423
decls_block::ClassLayout::readRecord(scratch, nameID, contextID,
34243424
isImplicit, isObjC,
34253425
requiresStoredPropertyInits,
34263426
inheritsSuperclassInitializers,
34273427
genericEnvID, superclassID,
34283428
rawAccessLevel, numConformances,
3429-
rawInheritedIDs);
3429+
numInheritedTypes,
3430+
rawInheritedAndDependencyIDs);
3431+
3432+
Identifier name = MF.getIdentifier(nameID);
3433+
3434+
for (TypeID dependencyID :
3435+
rawInheritedAndDependencyIDs.slice(numInheritedTypes)) {
3436+
auto dependency = MF.getTypeChecked(dependencyID);
3437+
if (!dependency) {
3438+
return llvm::make_error<TypeError>(
3439+
name, takeErrorInfo(dependency.takeError()));
3440+
}
3441+
}
34303442

34313443
auto DC = MF.getDeclContext(contextID);
34323444
if (declOrOffset.isComplete())
@@ -3436,10 +3448,8 @@ class swift::DeclDeserializer {
34363448
if (declOrOffset.isComplete())
34373449
return declOrOffset;
34383450

3439-
auto theClass = MF.createDecl<ClassDecl>(SourceLoc(),
3440-
MF.getIdentifier(nameID),
3441-
SourceLoc(), None, genericParams,
3442-
DC);
3451+
auto theClass = MF.createDecl<ClassDecl>(SourceLoc(), name, SourceLoc(),
3452+
None, genericParams, DC);
34433453
declOrOffset = theClass;
34443454

34453455
MF.configureGenericEnvironment(theClass, genericEnvID);
@@ -3463,7 +3473,8 @@ class swift::DeclDeserializer {
34633473

34643474
theClass->computeType();
34653475

3466-
handleInherited(theClass, rawInheritedIDs);
3476+
handleInherited(theClass,
3477+
rawInheritedAndDependencyIDs.slice(0, numInheritedTypes));
34673478

34683479
theClass->setMemberLoader(&MF, MF.DeclTypeCursor.GetCurrentBitNo());
34693480
theClass->setHasDestructor();

lib/Serialization/Serialization.cpp

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,22 +2697,39 @@ void Serializer::writeForeignErrorConvention(const ForeignErrorConvention &fec){
26972697
/// - \p decl is declared in an extension of a type that depends on
26982698
/// \p problemContext
26992699
static bool contextDependsOn(const NominalTypeDecl *decl,
2700-
const ModuleDecl *problemModule) {
2701-
return decl->getParentModule() == problemModule;
2700+
const DeclContext *problemContext) {
2701+
SmallPtrSet<const ExtensionDecl *, 8> seenExtensionDCs;
2702+
2703+
const DeclContext *dc = decl;
2704+
do {
2705+
if (dc == problemContext)
2706+
return true;
2707+
2708+
if (auto *extension = dyn_cast<ExtensionDecl>(dc)) {
2709+
if (extension->isChildContextOf(problemContext))
2710+
return true;
2711+
2712+
// Avoid cycles when Left.Nested depends on Right.Nested somehow.
2713+
bool isNewlySeen = seenExtensionDCs.insert(extension).second;
2714+
if (!isNewlySeen)
2715+
break;
2716+
dc = extension->getSelfNominalTypeDecl();
2717+
2718+
} else {
2719+
dc = dc->getParent();
2720+
}
2721+
} while (dc);
2722+
2723+
return false;
27022724
}
27032725

27042726
static void collectDependenciesFromType(llvm::SmallSetVector<Type, 4> &seen,
27052727
Type ty,
2706-
const ModuleDecl *excluding) {
2728+
const DeclContext *excluding) {
27072729
ty.visit([&](Type next) {
27082730
auto *nominal = next->getAnyNominal();
27092731
if (!nominal)
27102732
return;
2711-
// FIXME: Types in the same module are still important for enums. It's
2712-
// possible an enum element has a payload that references a type declaration
2713-
// from the same module that can't be imported (for whatever reason).
2714-
// However, we need a more robust handling of deserialization dependencies
2715-
// that can handle circularities. rdar://problem/32359173
27162733
if (contextDependsOn(nominal, excluding))
27172734
return;
27182735
seen.insert(nominal->getDeclaredInterfaceType());
@@ -2722,7 +2739,7 @@ static void collectDependenciesFromType(llvm::SmallSetVector<Type, 4> &seen,
27222739
static void
27232740
collectDependenciesFromRequirement(llvm::SmallSetVector<Type, 4> &seen,
27242741
const Requirement &req,
2725-
const ModuleDecl *excluding) {
2742+
const DeclContext *excluding) {
27262743
collectDependenciesFromType(seen, req.getFirstType(), excluding);
27272744
if (req.getKind() != RequirementKind::Layout)
27282745
collectDependenciesFromType(seen, req.getSecondType(), excluding);
@@ -3175,6 +3192,11 @@ void Serializer::writeDecl(const Decl *D) {
31753192
for (const EnumElementDecl *nextElt : theEnum->getAllElements()) {
31763193
if (!nextElt->hasAssociatedValues())
31773194
continue;
3195+
// FIXME: Types in the same module are still important for enums. It's
3196+
// possible an enum element has a payload that references a type
3197+
// declaration from the same module that can't be imported (for whatever
3198+
// reason). However, we need a more robust handling of deserialization
3199+
// dependencies that can handle circularities. rdar://problem/32359173
31783200
collectDependenciesFromType(dependencyTypes,
31793201
nextElt->getArgumentInterfaceType(),
31803202
/*excluding*/theEnum->getParentModule());
@@ -3219,12 +3241,29 @@ void Serializer::writeDecl(const Decl *D) {
32193241
auto conformances = theClass->getLocalConformances(
32203242
ConformanceLookupKind::NonInherited, nullptr);
32213243

3222-
SmallVector<TypeID, 4> inheritedTypes;
3244+
SmallVector<TypeID, 4> inheritedAndDependencyTypes;
32233245
for (auto inherited : theClass->getInherited()) {
32243246
assert(!inherited.getType()->hasArchetype());
3225-
inheritedTypes.push_back(addTypeRef(inherited.getType()));
3247+
inheritedAndDependencyTypes.push_back(addTypeRef(inherited.getType()));
32263248
}
32273249

3250+
llvm::SmallSetVector<Type, 4> dependencyTypes;
3251+
if (theClass->hasSuperclass()) {
3252+
// FIXME: Nested types can still be a problem here: it's possible that (for
3253+
// whatever reason) they won't be able to be deserialized, in which case
3254+
// we'll be in trouble forming the actual superclass type. However, we
3255+
// need a more robust handling of deserialization dependencies that can
3256+
// handle circularities. rdar://problem/50835214
3257+
collectDependenciesFromType(dependencyTypes, theClass->getSuperclass(),
3258+
/*excluding*/theClass);
3259+
}
3260+
for (Requirement req : theClass->getGenericRequirements()) {
3261+
collectDependenciesFromRequirement(dependencyTypes, req,
3262+
/*excluding*/nullptr);
3263+
}
3264+
for (Type ty : dependencyTypes)
3265+
inheritedAndDependencyTypes.push_back(addTypeRef(ty));
3266+
32283267
uint8_t rawAccessLevel =
32293268
getRawStableAccessLevel(theClass->getFormalAccess());
32303269

@@ -3245,7 +3284,8 @@ void Serializer::writeDecl(const Decl *D) {
32453284
addTypeRef(theClass->getSuperclass()),
32463285
rawAccessLevel,
32473286
conformances.size(),
3248-
inheritedTypes);
3287+
theClass->getInherited().size(),
3288+
inheritedAndDependencyTypes);
32493289

32503290
writeGenericParams(theClass->getGenericParams());
32513291
writeMembers(id, theClass->getMembers(), true);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@interface Root
2+
- (instancetype)init;
3+
@end
4+
5+
#if !BAD
6+
@interface DisappearingSuperclass : Root
7+
@end
8+
#else
9+
#endif

test/Serialization/Recovery/Inputs/custom-modules/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module IndirectlyImported {
99
module Overrides { header "Overrides.h" }
1010
module ProtocolInheritance { header "ProtocolInheritance.h" }
1111
module RenameAcrossVersions { header "RenameAcrossVersions.h" }
12+
module SuperclassObjC { header "SuperclassObjC.h" }
1213
module Typedefs { header "Typedefs.h" }
1314
module TypeRemovalObjC { header "TypeRemovalObjC.h" }
1415
module Types { header "Types.h" }
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-sil -enable-objc-interop -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules %s > /dev/null
3+
4+
// RUN: %target-swift-ide-test -enable-objc-interop -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck -check-prefix CHECK -check-prefix CHECK-ALWAYS %s
5+
6+
// RUN: %target-swift-ide-test -enable-objc-interop -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD | %FileCheck -check-prefix CHECK-RECOVERY -check-prefix CHECK-ALWAYS %s
7+
8+
// RUN: %target-swift-frontend -typecheck -enable-objc-interop -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST %s -verify
9+
10+
#if TEST
11+
12+
import Lib
13+
14+
func use(_: C2_CRTPish) {}
15+
func use(_: C3_NestingCRTPish) {}
16+
func use(_: C4_ExtensionNestingCRTPish) {}
17+
18+
// FIXME: Better to import the class and make it unavailable.
19+
func use(_: A1_Sub) {} // expected-error {{use of undeclared type 'A1_Sub'}}
20+
func use(_: A2_Grandchild) {} // expected-error {{use of undeclared type 'A2_Grandchild'}}
21+
22+
#else // TEST
23+
24+
import SuperclassObjC
25+
26+
// CHECK-LABEL: class A1_Sub : DisappearingSuperclass {
27+
// CHECK-RECOVERY-NOT: class A1_Sub
28+
public class A1_Sub: DisappearingSuperclass {}
29+
// CHECK-LABEL: class A2_Grandchild : A1_Sub {
30+
// CHECK-RECOVERY-NOT: class A2_Grandchild
31+
public class A2_Grandchild: A1_Sub {}
32+
33+
// CHECK-LABEL: class B_ConstrainedGeneric<T> where T : DisappearingSuperclass {
34+
// CHECK-RECOVERY-NOT: class B_ConstrainedGeneric
35+
public class B_ConstrainedGeneric<T> where T: DisappearingSuperclass {}
36+
37+
// CHECK-ALWAYS-LABEL: class C1_GenericBase<T> {
38+
public class C1_GenericBase<T> {}
39+
40+
// CHECK-ALWAYS-LABEL: class C2_CRTPish : C1_GenericBase<C2_CRTPish> {
41+
public class C2_CRTPish: C1_GenericBase<C2_CRTPish> {}
42+
// CHECK-ALWAYS-LABEL: class C3_NestingCRTPish : C1_GenericBase<C3_NestingCRTPish.Nested> {
43+
public class C3_NestingCRTPish: C1_GenericBase<C3_NestingCRTPish.Nested> {
44+
public struct Nested {}
45+
}
46+
// CHECK-ALWAYS-LABEL: class C4_ExtensionNestingCRTPish : C1_GenericBase<C4_ExtensionNestingCRTPish.Nested> {
47+
public class C4_ExtensionNestingCRTPish: C1_GenericBase<C4_ExtensionNestingCRTPish.Nested> {}
48+
extension C4_ExtensionNestingCRTPish {
49+
public struct Nested {}
50+
}
51+
52+
#endif // TEST

test/Serialization/Recovery/typedefs-in-enums.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,31 @@ public func producesOkayEnum() -> OkayEnum { return .noPayload }
110110
// CHECK-LABEL: func producesOkayEnum() -> OkayEnum
111111
// CHECK-RECOVERY-LABEL: func producesOkayEnum() -> OkayEnum
112112

113+
114+
extension Int /* or any imported type, really */ {
115+
public enum OkayEnumWithSelfRefs {
116+
public struct Nested {}
117+
indirect case selfRef(OkayEnumWithSelfRefs)
118+
case nested(Nested)
119+
}
120+
}
121+
// CHECK-LABEL: extension Int {
122+
// CHECK-NEXT: enum OkayEnumWithSelfRefs {
123+
// CHECK-NEXT: struct Nested {
124+
// CHECK-NEXT: init()
125+
// CHECK-NEXT: }
126+
// CHECK-NEXT: indirect case selfRef(Int.OkayEnumWithSelfRefs)
127+
// CHECK-NEXT: case nested(Int.OkayEnumWithSelfRefs.Nested)
128+
// CHECK-NEXT: }
129+
// CHECK-NEXT: }
130+
// CHECK-RECOVERY-LABEL: extension Int {
131+
// CHECK-RECOVERY-NEXT: enum OkayEnumWithSelfRefs {
132+
// CHECK-RECOVERY-NEXT: struct Nested {
133+
// CHECK-RECOVERY-NEXT: init()
134+
// CHECK-RECOVERY-NEXT: }
135+
// CHECK-RECOVERY-NEXT: indirect case selfRef(Int.OkayEnumWithSelfRefs)
136+
// CHECK-RECOVERY-NEXT: case nested(Int.OkayEnumWithSelfRefs.Nested)
137+
// CHECK-RECOVERY-NEXT: }
138+
// CHECK-RECOVERY-NEXT: }
139+
113140
#endif // TEST
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@interface Root
2+
- (instancetype)init;
3+
@end
4+
5+
#if !BAD
6+
@interface DisappearingSuperclass : Root
7+
@end
8+
#else
9+
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
module rdar40899824Helper { header "rdar40899824Helper.h" }
2+
module SuperclassObjC { header "SuperclassObjC.h" }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t/Lib.swiftmodule -I %S/Inputs/custom-modules %s
3+
4+
// FIXME: We need a way to handle the cyclic dependency here.
5+
// rdar://problem/50835214
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
7+
8+
public class GenericBase<T> {}
9+
public class Sub: GenericBase<Grandchild.Nested> {
10+
}
11+
public class Grandchild: Sub {
12+
public class Nested {}
13+
}

0 commit comments

Comments
 (0)