@@ -509,6 +509,55 @@ 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 = conformanceDC->mapTypeIntoContext (vd->getInterfaceType ());
533
+
534
+ if (auto referenceType = varType->getAs <ReferenceStorageType>()) {
535
+ // This is a weak/unowned/unmanaged var. Get the inner type before
536
+ // checking optionality.
537
+ varType = referenceType->getReferentType ();
538
+ }
539
+
540
+ bool useIfPresentVariant =
541
+ varType->getAnyNominal () == C.getOptionalDecl ();
542
+
543
+ if (useIfPresentVariant) {
544
+ // The type we request out of decodeIfPresent needs to be unwrapped
545
+ // one level.
546
+ // e.g. String? => decodeIfPresent(String.self, forKey: ...), not
547
+ // decodeIfPresent(String?.self, forKey: ...)
548
+ auto boundOptionalType =
549
+ dyn_cast<BoundGenericType>(varType->getCanonicalType ());
550
+ varType = boundOptionalType->getGenericArgs ()[0 ];
551
+ }
552
+
553
+ return std::make_tuple (vd, varType, useIfPresentVariant);
554
+ }
555
+ }
556
+ }
557
+
558
+ llvm_unreachable (" Should have found at least 1 var decl" );
559
+ }
560
+
512
561
// / Synthesizes the body for `func encode(to encoder: Encoder) throws`.
513
562
// /
514
563
// / \param encodeDecl The function decl whose body to synthesize.
@@ -531,7 +580,8 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
531
580
// }
532
581
533
582
// The enclosing type decl.
534
- auto *targetDecl = encodeDecl->getDeclContext ()->getSelfNominalTypeDecl ();
583
+ auto conformanceDC = encodeDecl->getDeclContext ();
584
+ auto *targetDecl = conformanceDC->getSelfNominalTypeDecl ();
535
585
536
586
auto *funcDC = cast<DeclContext>(encodeDecl);
537
587
auto &C = funcDC->getASTContext ();
@@ -590,16 +640,12 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
590
640
// Now need to generate `try container.encode(x, forKey: .x)` for all
591
641
// existing properties. Optional properties get `encodeIfPresent`.
592
642
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" );
643
+ VarDecl *varDecl;
644
+ Type varType; // not used in Encodable synthesis
645
+ bool useIfPresentVariant;
646
+
647
+ std::tie (varDecl, varType, useIfPresentVariant) =
648
+ lookupVarDeclForCodingKeysCase (conformanceDC, elt, targetDecl);
603
649
604
650
// self.x
605
651
auto *selfRef = DerivedConformance::createSelfDeclRef (encodeDecl);
@@ -613,16 +659,7 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
613
659
auto *keyExpr = new (C) DotSyntaxCallExpr (eltRef, SourceLoc (), metaTyRef);
614
660
615
661
// 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 ;
662
+ auto methodName = useIfPresentVariant ? C.Id_encodeIfPresent : C.Id_encode ;
626
663
627
664
SmallVector<Identifier, 2 > argNames{Identifier (), C.Id_forKey };
628
665
DeclName name (C, methodName, argNames);
@@ -831,42 +868,22 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
831
868
// for all existing properties. Optional properties get `decodeIfPresent`.
832
869
for (auto *elt : enumElements) {
833
870
VarDecl *varDecl;
834
- for (auto decl : targetDecl->lookupDirect (DeclName (elt->getName ())))
835
- if ((varDecl = dyn_cast<VarDecl>(decl)))
836
- break ;
871
+ Type varType;
872
+ bool useIfPresentVariant;
873
+
874
+ std::tie (varDecl, varType, useIfPresentVariant) =
875
+ lookupVarDeclForCodingKeysCase (conformanceDC, elt, targetDecl);
837
876
838
877
// Don't output a decode statement for a var let with a default value.
839
878
if (varDecl->isLet () && varDecl->getParentInitializer () != nullptr )
840
879
continue ;
841
880
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
- }
881
+ auto methodName =
882
+ useIfPresentVariant ? C.Id_decodeIfPresent : C.Id_decode ;
866
883
867
884
// Type.self (where Type === type(of: x))
868
885
// Calculating the metatype needs to happen after potential Optional
869
- // unwrapping above .
886
+ // unwrapping in lookupVarDeclForCodingKeysCase() .
870
887
auto *metaTyRef = TypeExpr::createImplicit (varType, C);
871
888
auto *targetExpr = new (C) DotSelfExpr (metaTyRef, SourceLoc (),
872
889
SourceLoc (), varType);
0 commit comments