Skip to content

Factor isDefaultInitializable logic for reuse #11902

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,23 @@ class PatternBindingDecl final : public Decl,

/// Does this binding declare something that requires storage?
bool hasStorage() const;

/// Determines whether this binding either has an initializer expression, or is
/// default initialized, without performing any type checking on it.
///
/// This is only valid to check for bindings which have storage.
bool isDefaultInitializable() const {
assert(hasStorage());

for (unsigned i = 0, e = getNumPatternEntries(); i < e; ++i)
if (!isDefaultInitializable(i))
return false;

return true;
}

/// Can the pattern at index i be default initialized?
bool isDefaultInitializable(unsigned i) const;

/// When the pattern binding contains only a single variable with no
/// destructuring, retrieve that variable.
Expand Down
88 changes: 88 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,94 @@ VarDecl *PatternBindingDecl::getSingleVar() const {
return nullptr;
}

/// Check whether the given type representation will be
/// default-initializable.
static bool isDefaultInitializable(const TypeRepr *typeRepr) {
// Look through most attributes.
if (const 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 (const auto tuple = dyn_cast<TupleTypeRepr>(typeRepr)) {
// ... but not variadic ones.
if (tuple->hasEllipsis())
return false;

for (const auto elt : tuple->getElements()) {
if (!isDefaultInitializable(elt.Type))
return false;
}

return true;
}

// Not default initializable.
return false;
}

// @NSManaged properties never get default initialized, nor do debugger
// variables and immutable properties.
bool isNeverDefaultInitializable(const Pattern *p) {
bool result = false;

p->forEachVariable([&](const VarDecl *var) {
if (var->getAttrs().hasAttribute<NSManagedAttr>())
return;

if (var->isDebuggerVar() ||
var->isLet())
result = true;
});

return result;
}

bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
const auto entry = getPatternList()[i];

// If it has an initializer expression, this is trivially true.
if (entry.getInit())
return true;

if (isNeverDefaultInitializable(entry.getPattern()))
return false;

// If the pattern is typed as optional (or tuples thereof), it is
// default initializable.
if (const auto typedPattern = dyn_cast<TypedPattern>(entry.getPattern())) {
if (const auto typeRepr = typedPattern->getTypeLoc().getTypeRepr()) {
if (::isDefaultInitializable(typeRepr))
return true;
} else if (typedPattern->isImplicit()) {
// Lazy vars have implicit storage assigned to back them. Because the
// storage is implicit, the pattern is typed and has a TypeLoc, but not a
// TypeRepr.
//
// All lazy storage is implicitly default initializable, though, because
// lazy backing storage is optional.
if (const auto *varDecl = typedPattern->getSingleVar())
// Lazy storage is never user accessible.
if (!varDecl->isUserAccessible())
if (typedPattern->getTypeLoc().getType()->getAnyOptionalObjectType())
return true;
}
}

// Otherwise, we can't default initialize this binding.
return false;
}

SourceLoc TopLevelCodeDecl::getStartLoc() const {
return Body->getStartLoc();
}
Expand Down
23 changes: 8 additions & 15 deletions lib/Sema/DerivedConformanceCodable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,26 +244,19 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
if (!properties.empty() &&
proto->isSpecificProtocol(KnownProtocolKind::Decodable)) {
for (auto it = properties.begin(); it != properties.end(); ++it) {
// If the var is default initializable, then it need not have an explicit
// initial value.
auto *varDecl = it->second;

// Optional vars (not lets!) have an implicit default value of nil.
if (!varDecl->isLet()) {
if (!varDecl->hasType())
tc.validateDecl(varDecl);

if (varDecl->hasType()) {
auto varTypeDecl = varDecl->getType()->getAnyNominal();
if (varTypeDecl == tc.Context.getOptionalDecl() ||
varTypeDecl == tc.Context.getImplicitlyUnwrappedOptionalDecl())
continue;
}
if (auto pbd = varDecl->getParentPatternBinding()) {
if (pbd->isDefaultInitializable())
continue;
}

if (varDecl->getParentInitializer() != nullptr) {
// Var has a default value.
if (varDecl->getParentInitializer())
continue;
}

// The var was not default initializable, and did not have an explicit
// initial value.
propertiesAreValid = false;
tc.diagnose(it->second->getLoc(), diag::codable_non_decoded_property_here,
proto->getDeclaredType(), it->first);
Expand Down
92 changes: 5 additions & 87 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,88 +822,6 @@ TypeChecker::handleSILGenericParams(GenericParamList *genericParams,
return parentEnv;
}

/// Check whether the given type representation will be
/// default-initializable.
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;
}

// @NSManaged properties never get default initialized, nor do debugger
// variables and immutable properties.
static bool isNeverDefaultInitializable(Pattern *p) {
bool result = false;

p->forEachVariable([&](VarDecl *var) {
if (var->getAttrs().hasAttribute<NSManagedAttr>())
return;

if (var->isDebuggerVar() ||
var->isLet())
result = true;
});

return result;
}

/// Determine whether the given pattern binding declaration either has
/// an initializer expression, or is default initialized, without performing
/// any type checking on it.
static bool isDefaultInitializable(PatternBindingDecl *pbd) {
assert(pbd->hasStorage());

for (auto entry : pbd->getPatternList()) {
// If it has an initializer expression, this is trivially true.
if (entry.getInit())
continue;

if (isNeverDefaultInitializable(entry.getPattern()))
return false;

// If the pattern is typed as optional (or tuples thereof), it is
// default initializable.
if (auto typedPattern = dyn_cast<TypedPattern>(entry.getPattern())) {
if (auto typeRepr = typedPattern->getTypeLoc().getTypeRepr())
if (isDefaultInitializable(typeRepr))
continue;
}

// Otherwise, we can't default initialize this binding.
return false;
}

return true;
}

/// Build a default initializer for the given type.
static Expr *buildDefaultInitializer(TypeChecker &tc, Type type) {
// Default-initialize optional types and weak values to 'nil'.
Expand Down Expand Up @@ -4204,7 +4122,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
TC.checkTypeModifyingDeclAttributes(var);

// Decide whether we should suppress default initialization.
if (isNeverDefaultInitializable(PBD->getPattern(i)))
if (!PBD->isDefaultInitializable(i))
continue;

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

if (pbd->isStatic() || !pbd->hasStorage() ||
isDefaultInitializable(pbd) || pbd->isInvalid())
pbd->isDefaultInitializable() || pbd->isInvalid())
continue;

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

if (pbd->isStatic() || !pbd->hasStorage() || isDefaultInitializable(pbd) ||
pbd->isInvalid())
if (pbd->isStatic() || !pbd->hasStorage() ||
pbd->isDefaultInitializable() || pbd->isInvalid())
continue;

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

// If we cannot default initialize the property, we cannot
// synthesize a default initializer for the class.
if (CheckDefaultInitializer && !isDefaultInitializable(pbd))
if (CheckDefaultInitializer && !pbd->isDefaultInitializable())
SuppressDefaultInitializer = true;
}
continue;
Expand Down