Skip to content

Commit cda3bf1

Browse files
author
Itai Ferber
committed
Factor isDefaultInitializable logic for reuse
Factors isDefaultInitializable logic from TypeChecker::addImplicitConstructors (and others) so it can be reused for Codable synthesis.
1 parent 2ad1d3a commit cda3bf1

File tree

4 files changed

+118
-102
lines changed

4 files changed

+118
-102
lines changed

include/swift/AST/Decl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,23 @@ class PatternBindingDecl final : public Decl,
19571957

19581958
/// Does this binding declare something that requires storage?
19591959
bool hasStorage() const;
1960+
1961+
/// Determines whether this binding either has an initializer expression, or is
1962+
/// default initialized, without performing any type checking on it.
1963+
///
1964+
/// This is only valid to check for bindings which have storage.
1965+
bool isDefaultInitializable() const {
1966+
assert(hasStorage());
1967+
1968+
for (unsigned i = 0, e = getNumPatternEntries(); i < e; ++i)
1969+
if (!isDefaultInitializable(i))
1970+
return false;
1971+
1972+
return true;
1973+
}
1974+
1975+
/// Can the pattern at index i be default initialized?
1976+
bool isDefaultInitializable(unsigned i) const;
19601977

19611978
/// When the pattern binding contains only a single variable with no
19621979
/// destructuring, retrieve that variable.

lib/AST/Decl.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,94 @@ VarDecl *PatternBindingDecl::getSingleVar() const {
11321132
return nullptr;
11331133
}
11341134

1135+
/// Check whether the given type representation will be
1136+
/// default-initializable.
1137+
static bool isDefaultInitializable(const TypeRepr *typeRepr) {
1138+
// Look through most attributes.
1139+
if (const auto attributed = dyn_cast<AttributedTypeRepr>(typeRepr)) {
1140+
// Weak ownership implies optionality.
1141+
if (attributed->getAttrs().getOwnership() == Ownership::Weak)
1142+
return true;
1143+
1144+
return isDefaultInitializable(attributed->getTypeRepr());
1145+
}
1146+
1147+
// Optional types are default-initializable.
1148+
if (isa<OptionalTypeRepr>(typeRepr) ||
1149+
isa<ImplicitlyUnwrappedOptionalTypeRepr>(typeRepr))
1150+
return true;
1151+
1152+
// Tuple types are default-initializable if all of their element
1153+
// types are.
1154+
if (const auto tuple = dyn_cast<TupleTypeRepr>(typeRepr)) {
1155+
// ... but not variadic ones.
1156+
if (tuple->hasEllipsis())
1157+
return false;
1158+
1159+
for (const auto elt : tuple->getElements()) {
1160+
if (!isDefaultInitializable(elt.Type))
1161+
return false;
1162+
}
1163+
1164+
return true;
1165+
}
1166+
1167+
// Not default initializable.
1168+
return false;
1169+
}
1170+
1171+
// @NSManaged properties never get default initialized, nor do debugger
1172+
// variables and immutable properties.
1173+
bool isNeverDefaultInitializable(const Pattern *p) {
1174+
bool result = false;
1175+
1176+
p->forEachVariable([&](const VarDecl *var) {
1177+
if (var->getAttrs().hasAttribute<NSManagedAttr>())
1178+
return;
1179+
1180+
if (var->isDebuggerVar() ||
1181+
var->isLet())
1182+
result = true;
1183+
});
1184+
1185+
return result;
1186+
}
1187+
1188+
bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
1189+
const auto entry = getPatternList()[i];
1190+
1191+
// If it has an initializer expression, this is trivially true.
1192+
if (entry.getInit())
1193+
return true;
1194+
1195+
if (isNeverDefaultInitializable(entry.getPattern()))
1196+
return false;
1197+
1198+
// If the pattern is typed as optional (or tuples thereof), it is
1199+
// default initializable.
1200+
if (const auto typedPattern = dyn_cast<TypedPattern>(entry.getPattern())) {
1201+
if (const auto typeRepr = typedPattern->getTypeLoc().getTypeRepr()) {
1202+
if (::isDefaultInitializable(typeRepr))
1203+
return true;
1204+
} else if (typedPattern->isImplicit()) {
1205+
// Lazy vars have implicit storage assigned to back them. Because the
1206+
// storage is implicit, the pattern is typed and has a TypeLoc, but not a
1207+
// TypeRepr.
1208+
//
1209+
// All lazy storage is implicitly default initializable, though, because
1210+
// lazy backing storage is optional.
1211+
if (const auto *varDecl = typedPattern->getSingleVar())
1212+
// Lazy storage is never user accessible.
1213+
if (!varDecl->isUserAccessible())
1214+
if (typedPattern->getTypeLoc().getType()->getAnyOptionalObjectType())
1215+
return true;
1216+
}
1217+
}
1218+
1219+
// Otherwise, we can't default initialize this binding.
1220+
return false;
1221+
}
1222+
11351223
SourceLoc TopLevelCodeDecl::getStartLoc() const {
11361224
return Body->getStartLoc();
11371225
}

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -244,26 +244,19 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
244244
if (!properties.empty() &&
245245
proto->isSpecificProtocol(KnownProtocolKind::Decodable)) {
246246
for (auto it = properties.begin(); it != properties.end(); ++it) {
247+
// If the var is default initializable, then it need not have an explicit
248+
// initial value.
247249
auto *varDecl = it->second;
248-
249-
// Optional vars (not lets!) have an implicit default value of nil.
250-
if (!varDecl->isLet()) {
251-
if (!varDecl->hasType())
252-
tc.validateDecl(varDecl);
253-
254-
if (varDecl->hasType()) {
255-
auto varTypeDecl = varDecl->getType()->getAnyNominal();
256-
if (varTypeDecl == tc.Context.getOptionalDecl() ||
257-
varTypeDecl == tc.Context.getImplicitlyUnwrappedOptionalDecl())
258-
continue;
259-
}
250+
if (auto pbd = varDecl->getParentPatternBinding()) {
251+
if (pbd->isDefaultInitializable())
252+
continue;
260253
}
261254

262-
if (varDecl->getParentInitializer() != nullptr) {
263-
// Var has a default value.
255+
if (varDecl->getParentInitializer())
264256
continue;
265-
}
266257

258+
// The var was not default initializable, and did not have an explicit
259+
// initial value.
267260
propertiesAreValid = false;
268261
tc.diagnose(it->second->getLoc(), diag::codable_non_decoded_property_here,
269262
proto->getDeclaredType(), it->first);

lib/Sema/TypeCheckDecl.cpp

Lines changed: 5 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -822,88 +822,6 @@ TypeChecker::handleSILGenericParams(GenericParamList *genericParams,
822822
return parentEnv;
823823
}
824824

825-
/// Check whether the given type representation will be
826-
/// default-initializable.
827-
static bool isDefaultInitializable(TypeRepr *typeRepr) {
828-
// Look through most attributes.
829-
if (auto attributed = dyn_cast<AttributedTypeRepr>(typeRepr)) {
830-
// Weak ownership implies optionality.
831-
if (attributed->getAttrs().getOwnership() == Ownership::Weak)
832-
return true;
833-
834-
return isDefaultInitializable(attributed->getTypeRepr());
835-
}
836-
837-
// Optional types are default-initializable.
838-
if (isa<OptionalTypeRepr>(typeRepr) ||
839-
isa<ImplicitlyUnwrappedOptionalTypeRepr>(typeRepr))
840-
return true;
841-
842-
// Tuple types are default-initializable if all of their element
843-
// types are.
844-
if (auto tuple = dyn_cast<TupleTypeRepr>(typeRepr)) {
845-
// ... but not variadic ones.
846-
if (tuple->hasEllipsis())
847-
return false;
848-
849-
for (auto elt : tuple->getElements()) {
850-
if (!isDefaultInitializable(elt.Type))
851-
return false;
852-
}
853-
854-
return true;
855-
}
856-
857-
// Not default initializable.
858-
return false;
859-
}
860-
861-
// @NSManaged properties never get default initialized, nor do debugger
862-
// variables and immutable properties.
863-
static bool isNeverDefaultInitializable(Pattern *p) {
864-
bool result = false;
865-
866-
p->forEachVariable([&](VarDecl *var) {
867-
if (var->getAttrs().hasAttribute<NSManagedAttr>())
868-
return;
869-
870-
if (var->isDebuggerVar() ||
871-
var->isLet())
872-
result = true;
873-
});
874-
875-
return result;
876-
}
877-
878-
/// Determine whether the given pattern binding declaration either has
879-
/// an initializer expression, or is default initialized, without performing
880-
/// any type checking on it.
881-
static bool isDefaultInitializable(PatternBindingDecl *pbd) {
882-
assert(pbd->hasStorage());
883-
884-
for (auto entry : pbd->getPatternList()) {
885-
// If it has an initializer expression, this is trivially true.
886-
if (entry.getInit())
887-
continue;
888-
889-
if (isNeverDefaultInitializable(entry.getPattern()))
890-
return false;
891-
892-
// If the pattern is typed as optional (or tuples thereof), it is
893-
// default initializable.
894-
if (auto typedPattern = dyn_cast<TypedPattern>(entry.getPattern())) {
895-
if (auto typeRepr = typedPattern->getTypeLoc().getTypeRepr())
896-
if (isDefaultInitializable(typeRepr))
897-
continue;
898-
}
899-
900-
// Otherwise, we can't default initialize this binding.
901-
return false;
902-
}
903-
904-
return true;
905-
}
906-
907825
/// Build a default initializer for the given type.
908826
static Expr *buildDefaultInitializer(TypeChecker &tc, Type type) {
909827
// Default-initialize optional types and weak values to 'nil'.
@@ -4204,7 +4122,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
42044122
TC.checkTypeModifyingDeclAttributes(var);
42054123

42064124
// Decide whether we should suppress default initialization.
4207-
if (isNeverDefaultInitializable(PBD->getPattern(i)))
4125+
if (!PBD->isDefaultInitializable(i))
42084126
continue;
42094127

42104128
auto type = PBD->getPattern(i)->getType();
@@ -4581,7 +4499,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
45814499
continue;
45824500

45834501
if (pbd->isStatic() || !pbd->hasStorage() ||
4584-
isDefaultInitializable(pbd) || pbd->isInvalid())
4502+
pbd->isDefaultInitializable() || pbd->isInvalid())
45854503
continue;
45864504

45874505
// The variables in this pattern have not been
@@ -8354,8 +8272,8 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
83548272
if (!pbd)
83558273
continue;
83568274

8357-
if (pbd->isStatic() || !pbd->hasStorage() || isDefaultInitializable(pbd) ||
8358-
pbd->isInvalid())
8275+
if (pbd->isStatic() || !pbd->hasStorage() ||
8276+
pbd->isDefaultInitializable() || pbd->isInvalid())
83598277
continue;
83608278

83618279
for (auto entry : pbd->getPatternList()) {
@@ -8638,7 +8556,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
86388556

86398557
// If we cannot default initialize the property, we cannot
86408558
// synthesize a default initializer for the class.
8641-
if (CheckDefaultInitializer && !isDefaultInitializable(pbd))
8559+
if (CheckDefaultInitializer && !pbd->isDefaultInitializable())
86428560
SuppressDefaultInitializer = true;
86438561
}
86448562
continue;

0 commit comments

Comments
 (0)