Skip to content

Commit 9c234d5

Browse files
committed
Make deriving CodingKeys Cheap
The semantic checks for CodingKeys are being duplicated across the value witness synthesis code paths. Just synthesize a CodingKeys enum and let validateCodingKeysEnum do the heavy lifting when we actually need to go emit diagnostics.
1 parent fefb800 commit 9c234d5

File tree

3 files changed

+27
-64
lines changed

3 files changed

+27
-64
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 19 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,6 @@ static bool validateCodingKeysEnum(DerivedConformance &derived,
199199
return propertiesAreValid;
200200
}
201201

202-
/// A type which has information about the validity of an encountered
203-
/// CodingKeys type.
204-
struct CodingKeysValidity {
205-
bool hasType;
206-
bool isValid;
207-
CodingKeysValidity(bool ht, bool iv) : hasType(ht), isValid(iv) {}
208-
};
209-
210202
/// Returns whether the given type has a valid nested \c CodingKeys enum.
211203
///
212204
/// If the type has an invalid \c CodingKeys entity, produces diagnostics to
@@ -216,12 +208,12 @@ struct CodingKeysValidity {
216208
/// enum.
217209
///
218210
/// \returns A \c CodingKeysValidity value representing the result of the check.
219-
static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) {
211+
static TypeDecl *getExistingValidCodingKeysDecl(DerivedConformance &derived) {
220212
auto &C = derived.Context;
221213
auto codingKeysDecls =
222214
derived.Nominal->lookupDirect(DeclName(C.Id_CodingKeys));
223215
if (codingKeysDecls.empty())
224-
return CodingKeysValidity(/*hasType=*/false, /*isValid=*/true);
216+
return nullptr;
225217

226218
// Only ill-formed code would produce multiple results for this lookup.
227219
// This would get diagnosed later anyway, so we're free to only look at the
@@ -232,7 +224,7 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) {
232224
if (!codingKeysTypeDecl) {
233225
result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here,
234226
derived.getProtocolType());
235-
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false);
227+
return nullptr;
236228
}
237229

238230
// CodingKeys may be a typealias. If so, follow the alias to its canonical
@@ -256,7 +248,7 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) {
256248
C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here,
257249
derived.getProtocolType());
258250

259-
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false);
251+
return nullptr;
260252
}
261253

262254
// CodingKeys must be an enum for synthesized conformance.
@@ -265,11 +257,15 @@ static CodingKeysValidity hasValidCodingKeysEnum(DerivedConformance &derived) {
265257
codingKeysTypeDecl->diagnose(
266258
diag::codable_codingkeys_type_is_not_an_enum_here,
267259
derived.getProtocolType());
268-
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false);
260+
return nullptr;
261+
}
262+
263+
// FIXME: This is a really expensive check.
264+
if (!validateCodingKeysEnum(derived, codingKeysEnum)) {
265+
return nullptr;
269266
}
270267

271-
bool valid = validateCodingKeysEnum(derived, codingKeysEnum);
272-
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/valid);
268+
return codingKeysEnum;
273269
}
274270

275271
/// Synthesizes a new \c CodingKeys enum based on the {En,De}codable members of
@@ -317,24 +313,12 @@ static EnumDecl *synthesizeCodingKeysEnum(DerivedConformance &derived) {
317313
if (!varDecl->isUserAccessible())
318314
continue;
319315

320-
// Despite creating the enum in the context of the type, we're
321-
// concurrently checking the variables for the current protocol
322-
// conformance being synthesized, for which we use the conformance
323-
// context, not the type.
324-
auto conformance = varConformsToCodable(derived.getConformanceContext(),
325-
varDecl, derived.Protocol);
326-
if (conformance.isInvalid()) {
327-
varDecl->diagnose(diag::codable_non_conforming_property_here,
328-
derived.getProtocolType(), varDecl->getType());
329-
allConform = false;
330-
} else {
331-
auto *elt = new (C) EnumElementDecl(SourceLoc(),
332-
getVarNameForCoding(varDecl),
333-
nullptr, SourceLoc(), nullptr,
334-
enumDecl);
335-
elt->setImplicit();
336-
enumDecl->addMember(elt);
337-
}
316+
auto *elt = new (C) EnumElementDecl(SourceLoc(),
317+
getVarNameForCoding(varDecl),
318+
nullptr, SourceLoc(), nullptr,
319+
enumDecl);
320+
elt->setImplicit();
321+
enumDecl->addMember(elt);
338322
}
339323

340324
if (!allConform)
@@ -1070,22 +1054,8 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
10701054
}
10711055
}
10721056

1073-
// If the target already has a valid CodingKeys enum, we won't need to
1074-
// synthesize one.
1075-
auto validity = hasValidCodingKeysEnum(derived);
1076-
1077-
// We found a type, but it wasn't valid.
1078-
if (!validity.isValid)
1079-
return false;
1080-
1081-
// We can try to synthesize a type here.
1082-
if (!validity.hasType) {
1083-
auto *synthesizedEnum = synthesizeCodingKeysEnum(derived);
1084-
if (!synthesizedEnum)
1085-
return false;
1086-
}
1087-
1088-
return true;
1057+
// Validate the CodingKeys enum.
1058+
return getExistingValidCodingKeysDecl(derived) != nullptr;
10891059
}
10901060

10911061
ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) {

test/decl/protocol/special/coding/class_codable_nonconforming_property.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,13 @@ class NonConformingClass : Codable { // expected-error {{type 'NonConformingClas
162162
// These lines have to be within the NonConformingClass type because
163163
// CodingKeys should be private.
164164
func foo() {
165-
// They should not get a CodingKeys type.
166-
let _ = NonConformingClass.CodingKeys.self // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
167-
let _ = NonConformingClass.CodingKeys.x // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
168-
let _ = NonConformingClass.CodingKeys.y // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
169-
let _ = NonConformingClass.CodingKeys.z // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
165+
let _ = NonConformingClass.CodingKeys.self
166+
let _ = NonConformingClass.CodingKeys.x
167+
let _ = NonConformingClass.CodingKeys.y
168+
let _ = NonConformingClass.CodingKeys.z // expected-error {{type 'NonConformingClass.CodingKeys' has no member 'z'}}
170169
}
171170
}
172171

173172
// They should not receive Codable methods.
174173
let _ = NonConformingClass.init(from:) // expected-error {{type 'NonConformingClass' has no member 'init(from:)'}}
175174
let _ = NonConformingClass.encode(to:) // expected-error {{type 'NonConformingClass' has no member 'encode(to:)'}}
176-
177-
// They should not get a CodingKeys type.
178-
let _ = NonConformingClass.CodingKeys.self // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}

test/decl/protocol/special/coding/struct_codable_nonconforming_property.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,13 @@ struct NonConformingStruct : Codable { // expected-error {{type 'NonConformingSt
163163
// CodingKeys should be private.
164164
func foo() {
165165
// They should not get a CodingKeys type.
166-
let _ = NonConformingStruct.CodingKeys.self // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
167-
let _ = NonConformingStruct.CodingKeys.x // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
168-
let _ = NonConformingStruct.CodingKeys.y // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
169-
let _ = NonConformingStruct.CodingKeys.z // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
166+
let _ = NonConformingStruct.CodingKeys.self
167+
let _ = NonConformingStruct.CodingKeys.x
168+
let _ = NonConformingStruct.CodingKeys.y
169+
let _ = NonConformingStruct.CodingKeys.z // expected-error {{type 'NonConformingStruct.CodingKeys' has no member 'z'}}
170170
}
171171
}
172172

173173
// They should not receive Codable methods.
174174
let _ = NonConformingStruct.init(from:) // expected-error {{type 'NonConformingStruct' has no member 'init(from:)'}}
175175
let _ = NonConformingStruct.encode(to:) // expected-error {{type 'NonConformingStruct' has no member 'encode(to:)'}}
176-
177-
// They should not get a CodingKeys type.
178-
let _ = NonConformingStruct.CodingKeys.self // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}

0 commit comments

Comments
 (0)