Skip to content

Commit 19015fa

Browse files
authored
Merge pull request swiftlang#23252 from brentdax/the-name-blame-game
2 parents db4994b + 1204c82 commit 19015fa

File tree

2 files changed

+76
-49
lines changed

2 files changed

+76
-49
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// Tests that, when synthesizing init(from:), we don't accidentally confuse
4+
// static and instance properties with the same name. (SR-10045)
5+
// The test fails if this file produces errors.
6+
7+
struct X: Codable {
8+
// The static property is a let with an initial value; Codable synthesis skips
9+
// instance properties that look like this.
10+
static let a: String = "a"
11+
12+
// The instance property has no initial value, so the definite initialization
13+
// checker will reject an init(from:) that doesn't decode a value for it.
14+
let a: String
15+
}

0 commit comments

Comments
 (0)