Skip to content

Commit fbdd907

Browse files
committed
Add a way to just synthesize CodingKeys
1 parent 9c234d5 commit fbdd907

File tree

6 files changed

+86
-2
lines changed

6 files changed

+86
-2
lines changed

include/swift/AST/Decl.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,15 @@ class alignas(1 << DeclAlignInBits) Decl {
467467
IsDebuggerAlias : 1
468468
);
469469

470-
SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1,
470+
SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1+1,
471471
/// Whether we have already added implicitly-defined initializers
472472
/// to this declaration.
473473
AddedImplicitInitializers : 1,
474474

475+
/// Whether we have already added the phantom CodingKeys
476+
/// nested requirement to this declaration.
477+
AddedPhantomCodingKeys : 1,
478+
475479
/// Whether there is are lazily-loaded conformances for this nominal type.
476480
HasLazyConformances : 1,
477481

@@ -3323,6 +3327,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
33233327
IterableDeclContext(IterableDeclContextKind::NominalTypeDecl)
33243328
{
33253329
Bits.NominalTypeDecl.AddedImplicitInitializers = false;
3330+
Bits.NominalTypeDecl.AddedPhantomCodingKeys = false;
33263331
ExtensionGeneration = 0;
33273332
Bits.NominalTypeDecl.HasLazyConformances = false;
33283333
Bits.NominalTypeDecl.IsComputingSemanticMembers = false;
@@ -3363,6 +3368,18 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
33633368
Bits.NominalTypeDecl.AddedImplicitInitializers = true;
33643369
}
33653370

3371+
/// Determine whether we have already attempted to add the
3372+
/// phantom CodingKeys type to this declaration.
3373+
bool addedPhantomCodingKeys() const {
3374+
return Bits.NominalTypeDecl.AddedPhantomCodingKeys;
3375+
}
3376+
3377+
/// Note that we have attempted to add the phantom CodingKeys type
3378+
/// to this declaration.
3379+
void setAddedPhantomCodingKeys() {
3380+
Bits.NominalTypeDecl.AddedPhantomCodingKeys = true;
3381+
}
3382+
33663383
/// getDeclaredType - Retrieve the type declared by this entity, without
33673384
/// any generic parameters bound if this is a generic type.
33683385
Type getDeclaredType() const;

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,3 +1139,21 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) {
11391139

11401140
return nullptr;
11411141
}
1142+
1143+
TypeDecl *DerivedConformance::derivePhantomCodingKeysRequirement() {
1144+
// We can only synthesize CodingKeys for structs and classes.
1145+
if (!isa<StructDecl>(Nominal) && !isa<ClassDecl>(Nominal))
1146+
return nullptr;
1147+
1148+
// Pull out a user-defined CodingKeys declaration.
1149+
auto result = Nominal->lookupDirect(DeclName(Context.Id_CodingKeys));
1150+
TypeDecl *keysDecl = nullptr;
1151+
if (!result.empty()) {
1152+
return dyn_cast<TypeDecl>(result.front());
1153+
} else {
1154+
// That failed, try to synthesize one.
1155+
keysDecl = synthesizeCodingKeysEnum(*this);
1156+
}
1157+
1158+
return keysDecl;
1159+
}

lib/Sema/DerivedConformances.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ class DerivedConformance {
189189
/// \returns the derived member, which will also be added to the type.
190190
ValueDecl *deriveDecodable(ValueDecl *requirement);
191191

192+
/// Derive the CodingKeys requirement for a value type.
193+
TypeDecl *derivePhantomCodingKeysRequirement();
194+
192195
/// Declare a read-only property.
193196
std::pair<VarDecl *, PatternBindingDecl *>
194197
declareDerivedProperty(Identifier name, Type propertyInterfaceType,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3391,6 +3391,14 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDerivation(
33913391
return ResolveWitnessResult::Missing;
33923392
}
33933393

3394+
// If the protocol has a phantom nested type requirement, resolve it now.
3395+
auto &attrs = Proto->getAttrs();
3396+
if (auto *IARA =
3397+
attrs.getAttribute<ImplicitlySynthesizesNestedRequirementAttr>()) {
3398+
(void)TypeChecker::derivePhantomWitness(DC, derivingTypeDecl,
3399+
Proto, IARA->Value);
3400+
}
3401+
33943402
// Attempt to derive the witness.
33953403
auto derived =
33963404
TypeChecker::deriveProtocolRequirement(DC, derivingTypeDecl, requirement);
@@ -5403,6 +5411,34 @@ Type TypeChecker::deriveTypeWitness(DeclContext *DC,
54035411
}
54045412
}
54055413

5414+
5415+
TypeDecl *TypeChecker::derivePhantomWitness(DeclContext *DC,
5416+
NominalTypeDecl *nominal,
5417+
ProtocolDecl *proto,
5418+
const StringRef Name) {
5419+
assert(proto->getASTContext().Id_CodingKeys.is(Name) &&
5420+
"CodingKeys is the only supported phantom requirement");
5421+
5422+
if (nominal->addedPhantomCodingKeys())
5423+
return nullptr;
5424+
nominal->setAddedPhantomCodingKeys();
5425+
5426+
auto knownKind = proto->getKnownProtocolKind();
5427+
if (!knownKind)
5428+
return nullptr;
5429+
5430+
auto Decl = DC->getInnermostDeclarationDeclContext();
5431+
5432+
DerivedConformance derived(nominal->getASTContext(), Decl, nominal, proto);
5433+
switch (*knownKind) {
5434+
case KnownProtocolKind::Decodable:
5435+
case KnownProtocolKind::Encodable:
5436+
return derived.derivePhantomCodingKeysRequirement();
5437+
default:
5438+
return nullptr;
5439+
}
5440+
}
5441+
54065442
namespace {
54075443
class DefaultWitnessChecker : public WitnessChecker {
54085444

lib/Sema/TypeChecker.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,17 @@ class TypeChecker final {
12141214
static Type deriveTypeWitness(DeclContext *DC, NominalTypeDecl *nominal,
12151215
AssociatedTypeDecl *assocType);
12161216

1217+
/// Derive an implicit type witness for a given "phantom" nested type
1218+
/// requirement that is known to the compiler but unstated as a
1219+
/// formal type requirement.
1220+
///
1221+
/// This exists to support Codable and only Codable. Do not expand its
1222+
/// usage outside of that domain.
1223+
static TypeDecl *derivePhantomWitness(DeclContext *DC,
1224+
NominalTypeDecl *nominal,
1225+
ProtocolDecl *proto,
1226+
const StringRef Name);
1227+
12171228
/// \name Name lookup
12181229
///
12191230
/// Routines that perform name lookup.

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ struct NonConformingStruct : Codable { // expected-error {{type 'NonConformingSt
162162
// These lines have to be within the NonConformingStruct type because
163163
// CodingKeys should be private.
164164
func foo() {
165-
// They should not get a CodingKeys type.
166165
let _ = NonConformingStruct.CodingKeys.self
167166
let _ = NonConformingStruct.CodingKeys.x
168167
let _ = NonConformingStruct.CodingKeys.y

0 commit comments

Comments
 (0)