@@ -509,6 +509,50 @@ static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC,
509509 C.AllocateCopy (argLabels));
510510}
511511
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+
512556// / Synthesizes the body for `func encode(to encoder: Encoder) throws`.
513557// /
514558// / \param encodeDecl The function decl whose body to synthesize.
@@ -531,7 +575,8 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
531575 // }
532576
533577 // The enclosing type decl.
534- auto *targetDecl = encodeDecl->getDeclContext ()->getSelfNominalTypeDecl ();
578+ auto conformanceDC = encodeDecl->getDeclContext ();
579+ auto *targetDecl = conformanceDC->getSelfNominalTypeDecl ();
535580
536581 auto *funcDC = cast<DeclContext>(encodeDecl);
537582 auto &C = funcDC->getASTContext ();
@@ -590,16 +635,12 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
590635 // Now need to generate `try container.encode(x, forKey: .x)` for all
591636 // existing properties. Optional properties get `encodeIfPresent`.
592637 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);
603644
604645 // self.x
605646 auto *selfRef = DerivedConformance::createSelfDeclRef (encodeDecl);
@@ -613,16 +654,7 @@ static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl, void *)
613654 auto *keyExpr = new (C) DotSyntaxCallExpr (eltRef, SourceLoc (), metaTyRef);
614655
615656 // 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 ;
626658
627659 SmallVector<Identifier, 2 > argNames{Identifier (), C.Id_forKey };
628660 DeclName name (C, methodName, argNames);
@@ -831,42 +863,22 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
831863 // for all existing properties. Optional properties get `decodeIfPresent`.
832864 for (auto *elt : enumElements) {
833865 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);
837871
838872 // Don't output a decode statement for a var let with a default value.
839873 if (varDecl->isLet () && varDecl->getParentInitializer () != nullptr )
840874 continue ;
841875
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 ;
866878
867879 // Type.self (where Type === type(of: x))
868880 // Calculating the metatype needs to happen after potential Optional
869- // unwrapping above .
881+ // unwrapping in lookupVarDeclForCodingKeysCase() .
870882 auto *metaTyRef = TypeExpr::createImplicit (varType, C);
871883 auto *targetExpr = new (C) DotSelfExpr (metaTyRef, SourceLoc (),
872884 SourceLoc (), varType);
0 commit comments