@@ -53,13 +53,118 @@ static Identifier getVarNameForCoding(VarDecl *var) {
53
53
return var->getName ();
54
54
}
55
55
56
+ // Create CodingKeys in the parent type always, because both
57
+ // Encodable and Decodable might want to use it, and they may have
58
+ // different conditional bounds. CodingKeys is simple and can't
59
+ // depend on those bounds.
60
+ //
61
+ // FIXME: Eventually we should find a way to expose this function to the lookup
62
+ // machinery so it no longer costs two protocol conformance lookups to retrieve
63
+ // CodingKeys. It will also help in our quest to separate semantic and parsed
64
+ // members.
65
+ static EnumDecl *addImplicitCodingKeys (NominalTypeDecl *target) {
66
+ auto &C = target->getASTContext ();
67
+ assert (target->lookupDirect (DeclName (C.Id_CodingKeys )).empty ());
68
+
69
+ // We want to look through all the var declarations of this type to create
70
+ // enum cases based on those var names.
71
+ auto *codingKeyProto = C.getProtocol (KnownProtocolKind::CodingKey);
72
+ auto codingKeyType = codingKeyProto->getDeclaredInterfaceType ();
73
+ TypeLoc protoTypeLoc[1 ] = {TypeLoc::withoutLoc (codingKeyType)};
74
+ ArrayRef<TypeLoc> inherited = C.AllocateCopy (protoTypeLoc);
75
+
76
+ auto *enumDecl = new (C) EnumDecl (SourceLoc (), C.Id_CodingKeys , SourceLoc (),
77
+ inherited, nullptr , target);
78
+ enumDecl->setImplicit ();
79
+ enumDecl->setSynthesized ();
80
+ enumDecl->setAccess (AccessLevel::Private);
81
+
82
+ // For classes which inherit from something Encodable or Decodable, we
83
+ // provide case `super` as the first key (to be used in encoding super).
84
+ auto *classDecl = dyn_cast<ClassDecl>(target);
85
+ if (superclassConformsTo (classDecl, KnownProtocolKind::Encodable) ||
86
+ superclassConformsTo (classDecl, KnownProtocolKind::Decodable)) {
87
+ // TODO: Ensure the class doesn't already have or inherit a variable named
88
+ // "`super`"; otherwise we will generate an invalid enum. In that case,
89
+ // diagnose and bail.
90
+ auto *super = new (C) EnumElementDecl (SourceLoc (), C.Id_super , nullptr ,
91
+ SourceLoc (), nullptr , enumDecl);
92
+ super->setImplicit ();
93
+ enumDecl->addMember (super);
94
+ }
95
+
96
+ for (auto *varDecl : target->getStoredProperties ()) {
97
+ if (!varDecl->isUserAccessible ()) {
98
+ continue ;
99
+ }
100
+
101
+ auto *elt =
102
+ new (C) EnumElementDecl (SourceLoc (), getVarNameForCoding (varDecl),
103
+ nullptr , SourceLoc (), nullptr , enumDecl);
104
+ elt->setImplicit ();
105
+ enumDecl->addMember (elt);
106
+ }
107
+
108
+ // Forcibly derive conformance to CodingKey.
109
+ TypeChecker::checkConformancesInContext (enumDecl);
110
+
111
+ // Add to the type.
112
+ target->addMember (enumDecl);
113
+
114
+ return enumDecl;
115
+ }
116
+
56
117
// / Validates the given CodingKeys enum decl by ensuring its cases are a 1-to-1
57
118
// / match with the stored vars of the given type.
58
- // /
59
- // / \param codingKeysDecl The \c CodingKeys enum decl to validate.
60
- static bool validateCodingKeysEnum (const DerivedConformance &derived,
61
- EnumDecl *codingKeysDecl) {
62
- auto conformanceDC = derived.getConformanceContext ();
119
+ static bool validateCodingKeysEnum (DerivedConformance &derived) {
120
+ auto &C = derived.Context ;
121
+ auto codingKeysDecls =
122
+ derived.Nominal ->lookupDirect (DeclName (C.Id_CodingKeys ));
123
+
124
+ if (codingKeysDecls.size () > 1 ) {
125
+ return false ;
126
+ }
127
+
128
+ ValueDecl *result = codingKeysDecls.empty ()
129
+ ? addImplicitCodingKeys (derived.Nominal )
130
+ : codingKeysDecls.front ();
131
+ auto *codingKeysTypeDecl = dyn_cast<TypeDecl>(result);
132
+ if (!codingKeysTypeDecl) {
133
+ result->diagnose (diag::codable_codingkeys_type_is_not_an_enum_here,
134
+ derived.getProtocolType ());
135
+ return false ;
136
+ }
137
+
138
+ // CodingKeys may be a typealias. If so, follow the alias to its canonical
139
+ // type.
140
+ auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType ();
141
+ if (isa<TypeAliasDecl>(codingKeysTypeDecl))
142
+ codingKeysTypeDecl = codingKeysType->getAnyNominal ();
143
+
144
+ // Ensure that the type we found conforms to the CodingKey protocol.
145
+ auto *codingKeyProto = C.getProtocol (KnownProtocolKind::CodingKey);
146
+ if (!TypeChecker::conformsToProtocol (codingKeysType, codingKeyProto,
147
+ derived.getConformanceContext ())) {
148
+ // If CodingKeys is a typealias which doesn't point to a valid nominal type,
149
+ // codingKeysTypeDecl will be nullptr here. In that case, we need to warn on
150
+ // the location of the usage, since there isn't an underlying type to
151
+ // diagnose on.
152
+ SourceLoc loc = codingKeysTypeDecl ? codingKeysTypeDecl->getLoc ()
153
+ : cast<TypeDecl>(result)->getLoc ();
154
+
155
+ C.Diags .diagnose (loc, diag::codable_codingkeys_type_does_not_conform_here,
156
+ derived.getProtocolType ());
157
+ return false ;
158
+ }
159
+
160
+ auto *codingKeysDecl =
161
+ dyn_cast_or_null<EnumDecl>(codingKeysType->getAnyNominal ());
162
+ if (!codingKeysDecl) {
163
+ codingKeysTypeDecl->diagnose (
164
+ diag::codable_codingkeys_type_is_not_an_enum_here,
165
+ derived.getProtocolType ());
166
+ return false ;
167
+ }
63
168
64
169
// Look through all var decls in the given type.
65
170
// * Filter out lazy/computed vars.
@@ -93,9 +198,10 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
93
198
}
94
199
95
200
// We have a property to map to. Ensure it's {En,De}codable.
96
- auto target =
97
- conformanceDC->mapTypeIntoContext (it->second ->getValueInterfaceType ());
98
- if (TypeChecker::conformsToProtocol (target, derived.Protocol , conformanceDC)
201
+ auto target = derived.getConformanceContext ()->mapTypeIntoContext (
202
+ it->second ->getValueInterfaceType ());
203
+ if (TypeChecker::conformsToProtocol (target, derived.Protocol ,
204
+ derived.getConformanceContext ())
99
205
.isInvalid ()) {
100
206
TypeLoc typeLoc = {
101
207
it->second ->getTypeReprOrParentPatternTypeRepr (),
@@ -138,164 +244,6 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
138
244
return propertiesAreValid;
139
245
}
140
246
141
- // / A type which has information about the validity of an encountered
142
- // / \c CodingKeys type.
143
- enum class CodingKeysClassification {
144
- // / A \c CodingKeys declaration was found, but it is invalid.
145
- Invalid,
146
- // / No \c CodingKeys declaration was found, so it must be synthesized.
147
- NeedsSynthesizedCodingKeys,
148
- // / A valid \c CodingKeys declaration was found.
149
- Valid,
150
- };
151
-
152
- // / Returns whether the given type has a valid nested \c CodingKeys enum.
153
- // /
154
- // / If the type has an invalid \c CodingKeys entity, produces diagnostics to
155
- // / complain about the error. In this case, the error result will be true -- in
156
- // / the case where we don't have a valid CodingKeys enum and have produced
157
- // / diagnostics here, we don't want to then attempt to synthesize a CodingKeys
158
- // / enum.
159
- // /
160
- // / \returns A \c CodingKeysValidity value representing the result of the check.
161
- static CodingKeysClassification
162
- classifyCodingKeys (const DerivedConformance &derived) {
163
- auto &C = derived.Context ;
164
- auto codingKeysDecls =
165
- derived.Nominal ->lookupDirect (DeclName (C.Id_CodingKeys ));
166
- if (codingKeysDecls.empty ()) {
167
- return CodingKeysClassification::NeedsSynthesizedCodingKeys;
168
- }
169
-
170
- // Only ill-formed code would produce multiple results for this lookup.
171
- // This would get diagnosed later anyway, so we're free to only look at the
172
- // first result here.
173
- auto result = codingKeysDecls.front ();
174
-
175
- auto *codingKeysTypeDecl = dyn_cast<TypeDecl>(result);
176
- if (!codingKeysTypeDecl) {
177
- result->diagnose (diag::codable_codingkeys_type_is_not_an_enum_here,
178
- derived.getProtocolType ());
179
- return CodingKeysClassification::Invalid;
180
- }
181
-
182
- // CodingKeys may be a typealias. If so, follow the alias to its canonical
183
- // type.
184
- auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType ();
185
- if (isa<TypeAliasDecl>(codingKeysTypeDecl))
186
- codingKeysTypeDecl = codingKeysType->getAnyNominal ();
187
-
188
- // Ensure that the type we found conforms to the CodingKey protocol.
189
- auto *codingKeyProto = C.getProtocol (KnownProtocolKind::CodingKey);
190
- if (!TypeChecker::conformsToProtocol (codingKeysType, codingKeyProto,
191
- derived.getConformanceContext ())) {
192
- // If CodingKeys is a typealias which doesn't point to a valid nominal type,
193
- // codingKeysTypeDecl will be nullptr here. In that case, we need to warn on
194
- // the location of the usage, since there isn't an underlying type to
195
- // diagnose on.
196
- SourceLoc loc = codingKeysTypeDecl ?
197
- codingKeysTypeDecl->getLoc () :
198
- cast<TypeDecl>(result)->getLoc ();
199
-
200
- C.Diags .diagnose (loc, diag::codable_codingkeys_type_does_not_conform_here,
201
- derived.getProtocolType ());
202
-
203
- return CodingKeysClassification::Invalid;
204
- }
205
-
206
- // CodingKeys must be an enum for synthesized conformance.
207
- auto *codingKeysEnum = dyn_cast<EnumDecl>(codingKeysTypeDecl);
208
- if (!codingKeysEnum) {
209
- codingKeysTypeDecl->diagnose (
210
- diag::codable_codingkeys_type_is_not_an_enum_here,
211
- derived.getProtocolType ());
212
- return CodingKeysClassification::Invalid;
213
- }
214
-
215
- return validateCodingKeysEnum (derived, codingKeysEnum)
216
- ? CodingKeysClassification::Valid
217
- : CodingKeysClassification::Invalid;
218
- }
219
-
220
- // / Synthesizes a new \c CodingKeys enum based on the {En,De}codable members of
221
- // / the given type (\c nullptr if unable to synthesize).
222
- // /
223
- // / If able to synthesize the enum, adds it directly to \c derived.Nominal.
224
- static EnumDecl *synthesizeCodingKeysEnum (DerivedConformance &derived) {
225
- auto &C = derived.Context ;
226
- // Create CodingKeys in the parent type always, because both
227
- // Encodable and Decodable might want to use it, and they may have
228
- // different conditional bounds. CodingKeys is simple and can't
229
- // depend on those bounds.
230
- auto target = derived.Nominal ;
231
-
232
- // We want to look through all the var declarations of this type to create
233
- // enum cases based on those var names.
234
- auto *codingKeyProto = C.getProtocol (KnownProtocolKind::CodingKey);
235
- auto codingKeyType = codingKeyProto->getDeclaredInterfaceType ();
236
- TypeLoc protoTypeLoc[1 ] = {TypeLoc::withoutLoc (codingKeyType)};
237
- ArrayRef<TypeLoc> inherited = C.AllocateCopy (protoTypeLoc);
238
-
239
- auto *enumDecl = new (C) EnumDecl (SourceLoc (), C.Id_CodingKeys , SourceLoc (),
240
- inherited, nullptr , target);
241
- enumDecl->setImplicit ();
242
- enumDecl->setSynthesized ();
243
- enumDecl->setAccess (AccessLevel::Private);
244
-
245
- // For classes which inherit from something Encodable or Decodable, we
246
- // provide case `super` as the first key (to be used in encoding super).
247
- auto *classDecl = dyn_cast<ClassDecl>(target);
248
- if (superclassConformsTo (classDecl, KnownProtocolKind::Encodable) ||
249
- superclassConformsTo (classDecl, KnownProtocolKind::Decodable)) {
250
- // TODO: Ensure the class doesn't already have or inherit a variable named
251
- // "`super`"; otherwise we will generate an invalid enum. In that case,
252
- // diagnose and bail.
253
- auto *super = new (C) EnumElementDecl (SourceLoc (), C.Id_super , nullptr ,
254
- SourceLoc (), nullptr , enumDecl);
255
- super->setImplicit ();
256
- enumDecl->addMember (super);
257
- }
258
-
259
- // Each of these vars needs a case in the enum. For each var decl, if the type
260
- // conforms to {En,De}codable, add it to the enum.
261
- bool allConform = true ;
262
- auto *conformanceDC = derived.getConformanceContext ();
263
- for (auto *varDecl : target->getStoredProperties ()) {
264
- if (!varDecl->isUserAccessible ()) {
265
- continue ;
266
- }
267
-
268
- auto target =
269
- conformanceDC->mapTypeIntoContext (varDecl->getValueInterfaceType ());
270
- if (TypeChecker::conformsToProtocol (target, derived.Protocol , conformanceDC)
271
- .isInvalid ()) {
272
- TypeLoc typeLoc = {
273
- varDecl->getTypeReprOrParentPatternTypeRepr (),
274
- varDecl->getType (),
275
- };
276
- varDecl->diagnose (diag::codable_non_conforming_property_here,
277
- derived.getProtocolType (), typeLoc);
278
- allConform = false ;
279
- } else {
280
- auto *elt =
281
- new (C) EnumElementDecl (SourceLoc (), getVarNameForCoding (varDecl),
282
- nullptr , SourceLoc (), nullptr , enumDecl);
283
- elt->setImplicit ();
284
- enumDecl->addMember (elt);
285
- }
286
- }
287
-
288
- if (!allConform)
289
- return nullptr ;
290
-
291
- // Forcibly derive conformance to CodingKey.
292
- TypeChecker::checkConformancesInContext (enumDecl);
293
-
294
- // Add to the type.
295
- target->addMember (enumDecl);
296
- return enumDecl;
297
- }
298
-
299
247
// / Fetches the \c CodingKeys enum nested in \c target, potentially reaching
300
248
// / through a typealias if the "CodingKeys" entity is a typealias.
301
249
// /
@@ -1060,17 +1008,13 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
1060
1008
}
1061
1009
}
1062
1010
1063
- switch (classifyCodingKeys (derived)) {
1064
- case CodingKeysClassification::Invalid:
1011
+ if (!validateCodingKeysEnum (derived)) {
1065
1012
return false ;
1066
- case CodingKeysClassification::NeedsSynthesizedCodingKeys: {
1067
- auto *synthesizedEnum = synthesizeCodingKeysEnum (derived);
1068
- if (!synthesizedEnum)
1069
- return false ;
1070
1013
}
1071
- LLVM_FALLTHROUGH;
1072
- case CodingKeysClassification::Valid:
1073
- return true ;
1014
+
1015
+ return true ;
1016
+ }
1017
+
1074
1018
static bool canDeriveCodable (NominalTypeDecl *NTD,
1075
1019
KnownProtocolKind Kind) {
1076
1020
assert (Kind == KnownProtocolKind::Encodable ||
0 commit comments