Skip to content

Commit aada983

Browse files
committed
Replace supersuperclassConformsTo
This more powerful primitive is capable of 1) Detecting invalid classes 2) Detecting invalid superclasses 3) Detecting circularity in inheritance hierarchies The attached crasher demonstrates the reason we need to validate all of these predicates. Simply put, a circular class hierarchy is always going to report the superclass conforms to any protocol with a declaration in the source. Leveraging this, one could construct any circular class hierarchy, and a conformance to Codable would immediately crash because we got as far as trying to build a CodingKeys enum with an absolutely nonsensical structure. This also has the added benefit of de-complecting an enormous amount of codable conformance code. rdar://66588925
1 parent 3b9b540 commit aada983

File tree

2 files changed

+34
-55
lines changed

2 files changed

+34
-55
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,15 @@ using namespace swift;
2929

3030
/// Returns whether the type represented by the given ClassDecl inherits from a
3131
/// type which conforms to the given protocol.
32-
///
33-
/// \param target The \c ClassDecl whose superclass to look up.
34-
///
35-
/// \param proto The protocol to check conformance for.
36-
static bool inheritsConformanceTo(ClassDecl *target, ProtocolDecl *proto) {
37-
if (!target->hasSuperclass())
32+
static bool superclassConformsTo(ClassDecl *target, KnownProtocolKind kpk) {
33+
if (!target || !target->getSuperclass() || target->hasCircularInheritance()) {
3834
return false;
39-
40-
auto *superclassDecl = target->getSuperclassDecl();
41-
auto *superclassModule = superclassDecl->getModuleContext();
42-
return (bool)superclassModule->lookupConformance(target->getSuperclass(),
43-
proto);
44-
}
45-
46-
/// Returns whether the superclass of the given class conforms to Encodable.
47-
///
48-
/// \param target The \c ClassDecl whose superclass to check.
49-
static bool superclassIsEncodable(ClassDecl *target) {
50-
auto &C = target->getASTContext();
51-
return inheritsConformanceTo(target,
52-
C.getProtocol(KnownProtocolKind::Encodable));
53-
}
54-
55-
/// Returns whether the superclass of the given class conforms to Decodable.
56-
///
57-
/// \param target The \c ClassDecl whose superclass to check.
58-
static bool superclassIsDecodable(ClassDecl *target) {
59-
auto &C = target->getASTContext();
60-
return inheritsConformanceTo(target,
61-
C.getProtocol(KnownProtocolKind::Decodable));
62-
}
63-
35+
}
36+
return !target->getSuperclassDecl()
37+
->getModuleContext()
38+
->lookupConformance(target->getSuperclass(),
39+
target->getASTContext().getProtocol(kpk))
40+
.isInvalid();
6441
}
6542

6643
/// Retrieve the variable name for the purposes of encoding/decoding.
@@ -134,25 +111,22 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
134111
// If there are any remaining properties which the CodingKeys did not cover,
135112
// we can skip them on encode. On decode, though, we can only skip them if
136113
// they have a default value.
137-
if (!properties.empty() &&
138-
derived.Protocol->isSpecificProtocol(KnownProtocolKind::Decodable)) {
139-
for (auto it = properties.begin(); it != properties.end(); ++it) {
140-
// If the var is default initializable, then it need not have an explicit
141-
// initial value.
142-
auto *varDecl = it->second;
143-
if (auto pbd = varDecl->getParentPatternBinding()) {
144-
if (pbd->isDefaultInitializable())
145-
continue;
114+
if (derived.Protocol->isSpecificProtocol(KnownProtocolKind::Decodable)) {
115+
for (auto &entry : properties) {
116+
const auto *pbd = entry.second->getParentPatternBinding();
117+
if (pbd && pbd->isDefaultInitializable()) {
118+
continue;
146119
}
147120

148-
if (varDecl->isParentInitialized())
121+
if (entry.second->isParentInitialized()) {
149122
continue;
123+
}
150124

151125
// The var was not default initializable, and did not have an explicit
152126
// initial value.
153127
propertiesAreValid = false;
154-
it->second->diagnose(diag::codable_non_decoded_property_here,
155-
derived.getProtocolType(), it->first);
128+
entry.second->diagnose(diag::codable_non_decoded_property_here,
129+
derived.getProtocolType(), entry.first);
156130
}
157131
}
158132

@@ -265,8 +239,8 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) {
265239
// For classes which inherit from something Encodable or Decodable, we
266240
// provide case `super` as the first key (to be used in encoding super).
267241
auto *classDecl = dyn_cast<ClassDecl>(target);
268-
if (classDecl &&
269-
(superclassIsEncodable(classDecl) || superclassIsDecodable(classDecl))) {
242+
if (superclassConformsTo(classDecl, KnownProtocolKind::Encodable) ||
243+
superclassConformsTo(classDecl, KnownProtocolKind::Decodable)) {
270244
// TODO: Ensure the class doesn't already have or inherit a variable named
271245
// "`super`"; otherwise we will generate an invalid enum. In that case,
272246
// diagnose and bail.
@@ -279,9 +253,11 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) {
279253
// Each of these vars needs a case in the enum. For each var decl, if the type
280254
// conforms to {En,De}codable, add it to the enum.
281255
bool allConform = true;
256+
auto *conformanceDC = derived.getConformanceContext();
282257
for (auto *varDecl : target->getStoredProperties()) {
283-
if (!varDecl->isUserAccessible())
258+
if (!varDecl->isUserAccessible()) {
284259
continue;
260+
}
285261

286262
auto target =
287263
conformanceDC->mapTypeIntoContext(varDecl->getValueInterfaceType());
@@ -578,8 +554,8 @@ deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *) {
578554
}
579555

580556
// Classes which inherit from something Codable should encode super as well.
581-
auto *classDecl = dyn_cast<ClassDecl>(targetDecl);
582-
if (classDecl && superclassIsEncodable(classDecl)) {
557+
if (superclassConformsTo(dyn_cast<ClassDecl>(targetDecl),
558+
KnownProtocolKind::Encodable)) {
583559
// Need to generate `try super.encode(to: container.superEncoder())`
584560

585561
// superEncoder()
@@ -661,8 +637,8 @@ static FuncDecl *deriveEncodable_encode(DerivedConformance &derived) {
661637

662638
// This method should be marked as 'override' for classes inheriting Encodable
663639
// conformance from a parent class.
664-
auto *classDecl = dyn_cast<ClassDecl>(derived.Nominal);
665-
if (classDecl && superclassIsEncodable(classDecl)) {
640+
if (superclassConformsTo(dyn_cast<ClassDecl>(derived.Nominal),
641+
KnownProtocolKind::Encodable)) {
666642
auto *attr = new (C) OverrideAttr(/*IsImplicit=*/true);
667643
encodeDecl->getAttrs().add(attr);
668644
}
@@ -869,7 +845,7 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
869845
// superclass is Decodable, or super.init() if it is not.
870846
if (auto *classDecl = dyn_cast<ClassDecl>(targetDecl)) {
871847
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
872-
if (superclassIsDecodable(classDecl)) {
848+
if (superclassConformsTo(classDecl, KnownProtocolKind::Decodable)) {
873849
// Need to generate `try super.init(from: container.superDecoder())`
874850

875851
// container.superDecoder
@@ -903,7 +879,8 @@ deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
903879
statements.push_back(tryExpr);
904880
} else {
905881
// The explicit constructor name is a compound name taking no arguments.
906-
DeclName initName(C, DeclBaseName::createConstructor(), ArrayRef<Identifier>());
882+
DeclName initName(C, DeclBaseName::createConstructor(),
883+
ArrayRef<Identifier>());
907884

908885
// We need to look this up in the superclass to see if it throws.
909886
auto result = superclassDecl->lookupDirect(initName);
@@ -1075,16 +1052,14 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
10751052
}
10761053
}
10771054

1078-
// If the target already has a valid CodingKeys enum, we won't need to
1079-
// synthesize one.
10801055
switch (classifyCodingKeys(derived)) {
10811056
case CodingKeysClassification::Invalid:
10821057
return false;
10831058
case CodingKeysClassification::NeedsSynthesizedCodingKeys: {
10841059
auto *synthesizedEnum = synthesizeCodingKeysEnum(derived);
10851060
if (!synthesizedEnum)
10861061
return false;
1087-
}
1062+
}
10881063
LLVM_FALLTHROUGH;
10891064
case CodingKeysClassification::Valid:
10901065
return true;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: not %target-swift-frontend -typecheck %s
2+
3+
class DataType: DataType {}
4+
extension DataType: Encodable {}

0 commit comments

Comments
 (0)