-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Codable synthesis: excluded vars shouldn’t need explicit value assignments #11854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Codable synthesis: excluded vars shouldn’t need explicit value assignments #11854
Conversation
Properties of Codable types which are optional and excluded via the CodingKeys enum should not require an explicit nil assignment, since in all other cases, optional vars get a default value of nil.
@swift-ci Please test |
if (varDecl->hasType()) { | ||
auto varTypeDecl = varDecl->getType()->getAnyNominal(); | ||
if (varTypeDecl == tc.Context.getOptionalDecl() || | ||
varTypeDecl == tc.Context.getImplicitlyUnwrappedOptionalDecl()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have TypeBase::getAnyOptionalObjectType
for this, but really I'm leery of putting this check in more than one place. Is there any way to reuse the logic in TypeChecker::addImplicitConstructors
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrose-apple That's likely a smarter solution. Are you interested in copying the logic into here, or factoring it out for reuse (e.g. adding isDefaultInitializable
or similar to PatternBindingDecl
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect it belongs on VarDecl rather than PatternBindingDecl, but the latter sounds good to me. Even if it's mostly just "is optional and is not immutable".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrose-apple I'll adapt this, then. I mention PatternBindingDecl
only because the logic in TypeChecker::addImplicitConstructors
revolves around that:
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
if (pbd->hasStorage() && !pbd->isStatic() && !pbd->isImplicit())
for (auto entry : pbd->getPatternList()) {
if (entry.getInit()) continue;
// If one of the bound variables is @NSManaged, go ahead no matter
// what.
bool CheckDefaultInitializer = true;
entry.getPattern()->forEachVariable([&](VarDecl *vd) {
if (vd->getAttrs().hasAttribute<NSManagedAttr>())
CheckDefaultInitializer = false;
});
// If we cannot default initialize the property, we cannot
// synthesize a default initializer for the class.
if (CheckDefaultInitializer && !isDefaultInitializable(pbd))
SuppressDefaultInitializer = true;
}
continue;
}
(isDefaultInitializable
takes a PatternBindingDecl
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, though I guess the implementation we really care about takes a TypeRepr *
:
static bool isDefaultInitializable(TypeRepr *typeRepr) {
// Look through most attributes.
if (auto attributed = dyn_cast<AttributedTypeRepr>(typeRepr)) {
// Weak ownership implies optionality.
if (attributed->getAttrs().getOwnership() == Ownership::Weak)
return true;
return isDefaultInitializable(attributed->getTypeRepr());
}
// Optional types are default-initializable.
if (isa<OptionalTypeRepr>(typeRepr) ||
isa<ImplicitlyUnwrappedOptionalTypeRepr>(typeRepr))
return true;
// Tuple types are default-initializable if all of their element
// types are.
if (auto tuple = dyn_cast<TupleTypeRepr>(typeRepr)) {
// ... but not variadic ones.
if (tuple->hasEllipsis())
return false;
for (auto elt : tuple->getElements()) {
if (!isDefaultInitializable(elt.Type))
return false;
}
return true;
}
// Not default initializable.
return false;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right. The existing logic is set up the way it is mostly because we don't have a way to get directly from a VarDecl to an initializer, but I guess it can stay that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also care about the isNeverDefaultInitializable
bit, so just looking at the TypeRepr isn't really sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes. How much of this do we care to expose, then? Should this just be exposed on PatternBindingDecl
, or is it helpful anywhere else to expose the intermediate layers too (on TypeRepr
and Pattern
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just using PatternBindingDecl is fine. We don't currently default-initialize anything but variables.
What's in this pull request?
Properties of
Codable
types which are optional and excluded via theCodingKeys
enum should not require an explicitnil
assignment, since in all other cases, optional vars get a default value ofnil
. The following code should get correct synthesis:Instead,
p3
andp4
currently prevent synthesis because they don't have an explicitnil
assignment.