@@ -509,6 +509,50 @@ static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC,
509
509
C.AllocateCopy (argLabels));
510
510
}
511
511
512
+ // / Looks up the property corresponding to the indicated coding key.
513
+ // /
514
+ // / \param conformanceDC The DeclContext we're generating code within.
515
+ // / \param elt The CodingKeys enum case.
516
+ // / \param targetDecl The type to look up properties in.
517
+ // /
518
+ // / \return A tuple containing the \c VarDecl for the property, the type that
519
+ // / should be passed when decoding it, and a boolean which is true if
520
+ // / \c encodeIfPresent/\c decodeIfPresent should be used for this property.
521
+ static std::tuple<VarDecl *, Type, bool >
522
+ lookupVarDeclForCodingKeysCase (DeclContext *conformanceDC,
523
+ EnumElementDecl *elt,
524
+ NominalTypeDecl *targetDecl) {
525
+ ASTContext &C = elt->getASTContext ();
526
+
527
+ for (auto decl : targetDecl->lookupDirect (DeclName (elt->getName ()))) {
528
+ if (auto *vd = dyn_cast<VarDecl>(decl)) {
529
+ if (!vd->isStatic ()) {
530
+ // This is the VarDecl we're looking for.
531
+
532
+ auto varType =
533
+ conformanceDC->mapTypeIntoContext (vd->getValueInterfaceType ());
534
+
535
+ bool useIfPresentVariant =
536
+ varType->getAnyNominal () == C.getOptionalDecl ();
537
+
538
+ if (useIfPresentVariant) {
539
+ // The type we request out of decodeIfPresent needs to be unwrapped
540
+ // one level.
541
+ // e.g. String? => decodeIfPresent(String.self, forKey: ...), not
542
+ // decodeIfPresent(String?.self, forKey: ...)
543
+ auto boundOptionalType =
544
+ dyn_cast<BoundGenericType>(varType->getCanonicalType ());
545
+ varType = boundOptionalType->getGenericArgs ()[0 ];
546
+ }
547
+
548
+ return std::make_tuple (vd, varType, useIfPresentVariant);
549
+ }
550
+ }
551
+ }
552
+
553
+ llvm_unreachable (" Should have found at least 1 var decl" );
554
+ }
555
+
512
556
// / Synthesizes the body for `func encode(to encoder: Encoder) throws`.
513
557
// /
514
558
// / \param encodeDecl The function decl whose body to synthesize.
@@ -531,7 +575,8 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
531
575
// }
532
576
533
577
// The enclosing type decl.
534
- auto *targetDecl = encodeDecl->getDeclContext ()->getSelfNominalTypeDecl ();
578
+ auto conformanceDC = encodeDecl->getDeclContext ();
579
+ auto *targetDecl = conformanceDC->getSelfNominalTypeDecl ();
535
580
536
581
auto *funcDC = cast<DeclContext>(encodeDecl);
537
582
auto &C = funcDC->getASTContext ();
@@ -590,16 +635,12 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
590
635
// Now need to generate `try container.encode(x, forKey: .x)` for all
591
636
// existing properties. Optional properties get `encodeIfPresent`.
592
637
for (auto *elt : codingKeysEnum->getAllElements ()) {
593
- VarDecl *varDecl = nullptr ;
594
- for (auto decl : targetDecl->lookupDirect (DeclName (elt->getName ()))) {
595
- if (auto *vd = dyn_cast<VarDecl>(decl)) {
596
- if (!vd->isStatic ()) {
597
- varDecl = vd;
598
- break ;
599
- }
600
- }
601
- }
602
- assert (varDecl && " Should have found at least 1 var decl" );
638
+ VarDecl *varDecl;
639
+ Type varType; // not used in Encodable synthesis
640
+ bool useIfPresentVariant;
641
+
642
+ std::tie (varDecl, varType, useIfPresentVariant) =
643
+ lookupVarDeclForCodingKeysCase (conformanceDC, elt, targetDecl);
603
644
604
645
// self.x
605
646
auto *selfRef = DerivedConformance::createSelfDeclRef (encodeDecl);
@@ -613,16 +654,7 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
613
654
auto *keyExpr = new (C) DotSyntaxCallExpr (eltRef, SourceLoc (), metaTyRef);
614
655
615
656
// encode(_:forKey:)/encodeIfPresent(_:forKey:)
616
- auto methodName = C.Id_encode ;
617
- auto varType = varDecl->getType ();
618
- if (auto referenceType = varType->getAs <ReferenceStorageType>()) {
619
- // This is a weak/unowned/unmanaged var. Get the inner type before
620
- // checking optionality.
621
- varType = referenceType->getReferentType ();
622
- }
623
-
624
- if (varType->getAnyNominal () == C.getOptionalDecl ())
625
- methodName = C.Id_encodeIfPresent ;
657
+ auto methodName = useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode ;
626
658
627
659
SmallVector<Identifier, 2 > argNames{Identifier (), C.Id_forKey };
628
660
DeclName name (C, methodName, argNames);
@@ -831,42 +863,22 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
831
863
// for all existing properties. Optional properties get `decodeIfPresent`.
832
864
for (auto *elt : enumElements) {
833
865
VarDecl *varDecl;
834
- for (auto decl : targetDecl->lookupDirect (DeclName (elt->getName ())))
835
- if ((varDecl = dyn_cast<VarDecl>(decl)))
836
- break ;
866
+ Type varType;
867
+ bool useIfPresentVariant;
868
+
869
+ std::tie (varDecl, varType, useIfPresentVariant) =
870
+ lookupVarDeclForCodingKeysCase (conformanceDC, elt, targetDecl);
837
871
838
872
// Don't output a decode statement for a var let with a default value.
839
873
if (varDecl->isLet () && varDecl->getParentInitializer () != nullptr )
840
874
continue ;
841
875
842
- // Potentially unwrap a layer of optionality from the var type. If the var
843
- // is Optional<T>, we want to decodeIfPresent(T.self, forKey: ...);
844
- // otherwise, we can just decode(T.self, forKey: ...).
845
- // This is also true if the type is an ImplicitlyUnwrappedOptional.
846
- auto varType = conformanceDC->mapTypeIntoContext (
847
- varDecl->getInterfaceType ());
848
- auto methodName = C.Id_decode ;
849
- if (auto referenceType = varType->getAs <ReferenceStorageType>()) {
850
- // This is a weak/unowned/unmanaged var. Get the inner type before
851
- // checking optionality.
852
- varType = referenceType->getReferentType ();
853
- }
854
-
855
- if (varType->getAnyNominal () == C.getOptionalDecl ()) {
856
- methodName = C.Id_decodeIfPresent ;
857
-
858
- // The type we request out of decodeIfPresent needs to be unwrapped
859
- // one level.
860
- // e.g. String? => decodeIfPresent(String.self, forKey: ...), not
861
- // decodeIfPresent(String?.self, forKey: ...)
862
- auto boundOptionalType =
863
- dyn_cast<BoundGenericType>(varType->getCanonicalType ());
864
- varType = boundOptionalType->getGenericArgs ()[0 ];
865
- }
876
+ auto methodName =
877
+ useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode ;
866
878
867
879
// Type.self (where Type === type(of: x))
868
880
// Calculating the metatype needs to happen after potential Optional
869
- // unwrapping above .
881
+ // unwrapping in lookupVarDeclForCodingKeysCase() .
870
882
auto *metaTyRef = TypeExpr::createImplicit (varType, C);
871
883
auto *targetExpr = new (C) DotSelfExpr (metaTyRef, SourceLoc (),
872
884
SourceLoc (), varType);
0 commit comments