Skip to content

Commit 30a7ec1

Browse files
authored
Merge pull request #41674 from rintaro/sema-codablesynth-rdar89150202
[CodeSynthesis] Improve synthesized `Decodable.init(from:)` for enums
2 parents 5fb34b4 + 1cfffb8 commit 30a7ec1

File tree

6 files changed

+91
-53
lines changed

6 files changed

+91
-53
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ IDENTIFIER(keyPath)
109109
IDENTIFIER(makeIterator)
110110
IDENTIFIER(makeAsyncIterator)
111111
IDENTIFIER(nestedContainer)
112+
IDENTIFIER(isEmpty)
112113
IDENTIFIER(Iterator)
113114
IDENTIFIER(AsyncIterator)
114115
IDENTIFIER(load)
@@ -124,6 +125,7 @@ IDENTIFIER(oldValue)
124125
IDENTIFIER(Optional)
125126
IDENTIFIER_(OptionalNilComparisonType)
126127
IDENTIFIER(parameter)
128+
IDENTIFIER(popFirst)
127129
IDENTIFIER(projected)
128130
IDENTIFIER(projectedValue)
129131
IDENTIFIER(Protocol)
@@ -149,7 +151,6 @@ IDENTIFIER(Type)
149151
IDENTIFIER(type)
150152
IDENTIFIER(typeMismatch)
151153
IDENTIFIER(underlyingError)
152-
IDENTIFIER(unsafelyUnwrapped)
153154
IDENTIFIER(Value)
154155
IDENTIFIER(value)
155156
IDENTIFIER_WITH_NAME(value_, "_value")

include/swift/AST/KnownStdlibTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ KNOWN_STDLIB_TYPE_DECL(String, NominalTypeDecl, 0)
4949
KNOWN_STDLIB_TYPE_DECL(StaticString, NominalTypeDecl, 0)
5050
KNOWN_STDLIB_TYPE_DECL(Substring, NominalTypeDecl, 0)
5151
KNOWN_STDLIB_TYPE_DECL(Array, NominalTypeDecl, 1)
52+
KNOWN_STDLIB_TYPE_DECL(ArraySlice, NominalTypeDecl, 1)
5253
KNOWN_STDLIB_TYPE_DECL(_ContiguousArrayStorage, ClassDecl, 1)
5354
KNOWN_STDLIB_TYPE_DECL(Set, NominalTypeDecl, 1)
5455
KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1)

lib/AST/ASTPrinter.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4917,20 +4917,22 @@ void PrintAST::visitRepeatWhileStmt(RepeatWhileStmt *stmt) {
49174917
visit(stmt->getCond());
49184918
}
49194919

4920-
void PrintAST::printStmtCondition(StmtCondition stmt) {
4921-
for (auto elt : stmt) {
4922-
if (auto pattern = elt.getPatternOrNull()) {
4923-
printPattern(pattern);
4924-
auto initializer = elt.getInitializer();
4925-
if (initializer) {
4926-
Printer << " = ";
4927-
visit(initializer);
4928-
}
4929-
}
4930-
else if (auto boolean = elt.getBooleanOrNull()) {
4931-
visit(boolean);
4932-
}
4933-
}
4920+
void PrintAST::printStmtCondition(StmtCondition condition) {
4921+
interleave(
4922+
condition,
4923+
[&](StmtConditionElement &elt) {
4924+
if (auto pattern = elt.getPatternOrNull()) {
4925+
printPattern(pattern);
4926+
auto initializer = elt.getInitializer();
4927+
if (initializer) {
4928+
Printer << " = ";
4929+
visit(initializer);
4930+
}
4931+
} else if (auto boolean = elt.getBooleanOrNull()) {
4932+
visit(boolean);
4933+
}
4934+
},
4935+
[&] { Printer << ", "; });
49344936
}
49354937

49364938
void PrintAST::visitDoStmt(DoStmt *stmt) {

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,13 +1552,14 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) {
15521552
//
15531553
// @derived init(from decoder: Decoder) throws {
15541554
// let container = try decoder.container(keyedBy: CodingKeys.self)
1555-
// if container.allKeys.count != 1 {
1555+
// var allKeys = ArraySlice(container.allKeys)
1556+
// guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
15561557
// let context = DecodingError.Context(
15571558
// codingPath: container.codingPath,
15581559
// debugDescription: "Invalid number of keys found, expected one.")
15591560
// throw DecodingError.typeMismatch(Foo.self, context)
15601561
// }
1561-
// switch container.allKeys.first {
1562+
// switch onlyKey {
15621563
// case .bar:
15631564
// let nestedContainer = try container.nestedContainer(
15641565
// keyedBy: BarCodingKeys.self, forKey: .bar)
@@ -1597,15 +1598,67 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) {
15971598
initDecl->getParameters()->get(0), codingKeysEnum, statements,
15981599
/*throws*/ true);
15991600

1601+
// generate: var allKeys = ArraySlice(container.allKeys);
1602+
auto *allKeysDecl =
1603+
new (C) VarDecl(/*IsStatic=*/false, VarDecl::Introducer::Var,
1604+
SourceLoc(), C.Id_allKeys, funcDC);
1605+
allKeysDecl->setImplicit();
1606+
allKeysDecl->setSynthesized();
1607+
{
1608+
auto *arraySliceRef =
1609+
new (C) DeclRefExpr(ConcreteDeclRef(C.getArraySliceDecl()),
1610+
DeclNameLoc(), /*Implicit=*/true);
1611+
auto *containerAllKeys =
1612+
UnresolvedDotExpr::createImplicit(C, containerExpr, C.Id_allKeys);
1613+
auto *argList = ArgumentList::createImplicit(
1614+
C, {Argument::unlabeled(containerAllKeys)});
1615+
auto *init = CallExpr::createImplicit(C, arraySliceRef, argList);
1616+
1617+
auto *allKeysPattern = NamedPattern::createImplicit(C, allKeysDecl);
1618+
auto *allKeysBindingDecl = PatternBindingDecl::createImplicit(
1619+
C, StaticSpellingKind::None, allKeysPattern, init, funcDC);
1620+
1621+
statements.push_back(allKeysBindingDecl);
1622+
statements.push_back(allKeysDecl);
1623+
}
1624+
16001625
// generate:
1601-
//
1602-
// if container.allKeys.count != 1 {
1626+
// guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
16031627
// let context = DecodingError.Context(
16041628
// codingPath: container.codingPath,
16051629
// debugDescription: "Invalid number of keys found, expected
16061630
// one.")
16071631
// throw DecodingError.typeMismatch(Foo.self, context)
16081632
// }
1633+
auto *theKeyDecl =
1634+
new (C) VarDecl(/*IsStatic=*/false, VarDecl::Introducer::Let,
1635+
SourceLoc(), C.getIdentifier("onlyKey"), funcDC);
1636+
theKeyDecl->setImplicit();
1637+
theKeyDecl->setSynthesized();
1638+
1639+
SmallVector<StmtConditionElement, 2> guardElements;
1640+
{
1641+
auto *allKeysExpr =
1642+
new (C) DeclRefExpr(ConcreteDeclRef(allKeysDecl), DeclNameLoc(),
1643+
/*Implicit=*/true);
1644+
1645+
// generate: let onlyKey = allKeys.popFirst;
1646+
auto *allKeysPopFisrtCallExpr = CallExpr::createImplicitEmpty(
1647+
C, UnresolvedDotExpr::createImplicit(C, allKeysExpr, C.Id_popFirst));
1648+
1649+
auto *theKeyPattern = BindingPattern::createImplicit(
1650+
C, /*isLet=*/true, NamedPattern::createImplicit(C, theKeyDecl));
1651+
1652+
guardElements.emplace_back(SourceLoc(), theKeyPattern,
1653+
allKeysPopFisrtCallExpr);
1654+
1655+
// generate: allKeys.isEmpty;
1656+
auto *allKeysIsEmptyExpr =
1657+
UnresolvedDotExpr::createImplicit(C, allKeysExpr, C.Id_isEmpty);
1658+
1659+
guardElements.emplace_back(allKeysIsEmptyExpr);
1660+
}
1661+
16091662
auto *targetType = TypeExpr::createImplicit(
16101663
funcDC->mapTypeIntoContext(targetEnum->getDeclaredInterfaceType()), C);
16111664
auto *targetTypeExpr =
@@ -1615,44 +1668,21 @@ deriveBodyDecodable_enum_init(AbstractFunctionDecl *initDecl, void *) {
16151668
C, containerExpr, C.getDecodingErrorDecl(), C.Id_typeMismatch,
16161669
targetTypeExpr, "Invalid number of keys found, expected one.");
16171670

1618-
// container.allKeys
1619-
auto *allKeysExpr =
1620-
UnresolvedDotExpr::createImplicit(C, containerExpr, C.Id_allKeys);
1621-
1622-
// container.allKeys.count
1623-
auto *keysCountExpr =
1624-
UnresolvedDotExpr::createImplicit(C, allKeysExpr, C.Id_count);
1625-
1626-
// container.allKeys.count == 1
1627-
auto *cmpFunc = C.getEqualIntDecl();
1628-
auto *fnType = cmpFunc->getInterfaceType()->castTo<FunctionType>();
1629-
auto *cmpFuncExpr = new (C)
1630-
DeclRefExpr(cmpFunc, DeclNameLoc(),
1631-
/*implicit*/ true, AccessSemantics::Ordinary, fnType);
1632-
auto *oneExpr = IntegerLiteralExpr::createFromUnsigned(C, 1);
1633-
1634-
auto *cmpExpr = BinaryExpr::create(C, keysCountExpr, cmpFuncExpr, oneExpr,
1635-
/*implicit*/ true);
1636-
cmpExpr->setThrows(false);
1637-
16381671
auto *guardBody = BraceStmt::create(C, SourceLoc(), {throwStmt},
16391672
SourceLoc(), /* Implicit */ true);
16401673

1641-
auto *guardStmt = new (C)
1642-
GuardStmt(SourceLoc(), cmpExpr, guardBody, /* Implicit */ true, C);
1674+
auto *guardStmt =
1675+
new (C) GuardStmt(SourceLoc(), C.AllocateCopy(guardElements), guardBody,
1676+
/* Implicit */ true);
16431677

16441678
statements.push_back(guardStmt);
16451679

1646-
// generate: switch container.allKeys.first { }
1647-
auto *firstExpr =
1648-
UnresolvedDotExpr::createImplicit(C, allKeysExpr, C.Id_first);
1649-
1650-
// generate: switch container.allKeys.first.unsafelyUnwrapped { }
1651-
auto *unwrapped =
1652-
UnresolvedDotExpr::createImplicit(C, firstExpr, C.Id_unsafelyUnwrapped);
1680+
// generate: switch onlyKey { }
1681+
auto *theKeyExpr = new (C) DeclRefExpr(ConcreteDeclRef(theKeyDecl),
1682+
DeclNameLoc(), /*Implicit=*/true);
16531683

16541684
auto switchStmt = createEnumSwitch(
1655-
C, funcDC, unwrapped, targetEnum, codingKeysEnum,
1685+
C, funcDC, theKeyExpr, targetEnum, codingKeysEnum,
16561686
/*createSubpattern*/ false,
16571687
[&](auto *elt, auto *codingKeyCase,
16581688
auto payloadVars) -> std::tuple<EnumElementDecl *, BraceStmt *> {

test/refactoring/AddCodableImplementation/Outputs/enum/codable.swift.expected

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ enum Payload: Codable {
1818
init(from decoder: Decoder) throws {
1919
let container = try decoder.container(keyedBy: Payload.CodingKeys.self)
2020

21-
guard container.allKeys.count == 1 else {
21+
var allKeys = ArraySlice(container.allKeys)
22+
23+
guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
2224
throw DecodingError.typeMismatch(Payload.self, DecodingError.Context.init(codingPath: container.codingPath, debugDescription: "Invalid number of keys found, expected one.", underlyingError: nil))
2325
}
24-
switch container.allKeys.first.unsafelyUnwrapped {
26+
switch onlyKey {
2527
case .plain:
2628

2729
let nestedContainer = try container.nestedContainer(keyedBy: Payload.PlainCodingKeys.self, forKey: Payload.CodingKeys.plain)

test/refactoring/AddCodableImplementation/Outputs/enum/decodable.swift.expected

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ enum Payload_D: Decodable {
2323
init(from decoder: Decoder) throws {
2424
let container = try decoder.container(keyedBy: Payload_D.CodingKeys.self)
2525

26-
guard container.allKeys.count == 1 else {
26+
var allKeys = ArraySlice(container.allKeys)
27+
28+
guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
2729
throw DecodingError.typeMismatch(Payload_D.self, DecodingError.Context.init(codingPath: container.codingPath, debugDescription: "Invalid number of keys found, expected one.", underlyingError: nil))
2830
}
29-
switch container.allKeys.first.unsafelyUnwrapped {
31+
switch onlyKey {
3032
case .plain:
3133

3234
let nestedContainer = try container.nestedContainer(keyedBy: Payload_D.PlainCodingKeys.self, forKey: Payload_D.CodingKeys.plain)

0 commit comments

Comments
 (0)