Skip to content

Centralize the definition of isMemberwiseInitialized() #24363

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 2 commits into from
Apr 30, 2019
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
12 changes: 11 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5142,7 +5142,17 @@ class VarDecl : public AbstractStorageDecl {

/// Returns true if the name is the self identifier and is implicit.
bool isSelfParameter() const;


/// Determine whether this property will be part of the implicit memberwise
/// initializer.
///
/// \param preferDeclaredProperties When encountering a `lazy` property
/// or a property that has an attached property delegate, prefer the
/// actual declared property (which may or may not be considered "stored"
/// as the moment) to the backing storage property. Otherwise, the stored
/// backing property will be treated as the member-initialized property.
bool isMemberwiseInitialized(bool preferDeclaredProperties) const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == DeclKind::Var || D->getKind() == DeclKind::Param;
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Sema/IDETypeChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,6 @@ namespace swift {
/// \param DC The DeclContext from which the subscript is being referenced.
Optional<Type> getRootTypeOfKeypathDynamicMember(SubscriptDecl *subscript,
const DeclContext *DC);

/// Determine whether the given property is part of the memberwise initializer
/// for a struct.
bool isMemberwiseInitialized(VarDecl *var);
}

#endif
55 changes: 55 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5254,6 +5254,61 @@ bool VarDecl::isSelfParameter() const {
return false;
}

/// Whether the given variable is the backing storage property for
/// a declared property that is either `lazy` or has an attached
/// property delegate.
static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
if (var->getOriginalDelegatedProperty())
return true;

auto name = var->getName();
if (name.empty())
return false;

return name.str().startswith("$__lazy_storage_$_");
}

/// Whether the given variable
static bool isDeclaredPropertyWithBackingStorage(const VarDecl *var) {
if (var->getAttrs().hasAttribute<LazyAttr>())
return true;

if (var->getAttachedPropertyDelegate())
return true;

return false;
}

bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
// Only non-static properties in type context can be part of a memberwise
// initializer.
if (!getDeclContext()->isTypeContext() || isStatic())
return false;

// If this is a stored property, and not a backing property in a case where
// we only want to see the declared properties, it can be memberwise
// initialized.
if (hasStorage() && preferDeclaredProperties &&
isBackingStorageForDeclaredProperty(this))
return false;

// If this is a computed property, it's not memberwise initialized unless
// the caller has asked for the declared properties and it is either a
// `lazy` property or a property with an attached delegate.
if (!hasStorage() &&
!(preferDeclaredProperties &&
isDeclaredPropertyWithBackingStorage(this)))
return false;

// Initialized 'let' properties have storage, but don't get an argument
// to the memberwise initializer since they already have an initial
// value that cannot be overridden.
if (isLet() && isParentInitialized())
return false;

return true;
}

void VarDecl::setSpecifier(Specifier specifier) {
Bits.VarDecl.Specifier = static_cast<unsigned>(specifier);
setSupportsMutationIfStillStored(
Expand Down
9 changes: 5 additions & 4 deletions lib/Index/Index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,12 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
return;

unsigned CurLabel = 0;
for (auto Prop : TypeContext->getStoredProperties()) {
if (auto Original = Prop->getOriginalDelegatedProperty())
Prop = Original;
for (auto Member : TypeContext->getMembers()) {
auto Prop = dyn_cast<VarDecl>(Member);
if (!Prop)
continue;

if (!isMemberwiseInitialized(Prop))
if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
continue;

if (CurLabel == LabelLocs.size())
Expand Down
18 changes: 10 additions & 8 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M);
SILValue v;

// An initialized 'let' property has a single value specified by the
// initializer - it doesn't come from an argument.
if (!field->isStatic() && field->isLet() && field->getParentInitializer()) {
// Cleanup after this initialization.
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
v = SGF.emitRValue(field->getParentInitializer())
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
} else {
// If it's memberwise initialized, do so now.
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) {
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
assert(elti != eltEnd && "number of args does not match number of fields");
(void)eltEnd;
Expand All @@ -237,6 +231,14 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
v = std::move(*elti).forwardAsSingleStorageValue(SGF, fieldTy, Loc);
}
++elti;
} else {
// Otherwise, use its initializer.
assert(field->isParentInitialized());

// Cleanup after this initialization.
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
v = SGF.emitRValue(field->getParentInitializer())
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
}

eltValues.push_back(v);
Expand Down
22 changes: 1 addition & 21 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2050,26 +2050,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty);
}

bool swift::isMemberwiseInitialized(VarDecl *var) {
// Implicit, computed, and static properties are not initialized.
// The exception is lazy properties, which due to batch mode we may or
// may not have yet finalized, so they may currently be "stored" or
// "computed" in the current AST state.
if (var->isImplicit() || var->isStatic())
return false;

if (!var->hasStorage() && !var->getAttrs().hasAttribute<LazyAttr>() &&
!var->getAttachedPropertyDelegate())
return false;

// Initialized 'let' properties have storage, but don't get an argument
// to the memberwise initializer since they already have an initial
// value that cannot be overridden.
if (var->isLet() && var->isParentInitialized())
return false;

return true;
}
/// Create an implicit struct or class constructor.
///
/// \param decl The struct or class for which a constructor will be created.
Expand Down Expand Up @@ -2097,7 +2077,7 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
if (!var)
continue;

if (!isMemberwiseInitialized(var))
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
continue;

accessLevel = std::min(accessLevel, var->getFormalAccess());
Expand Down
2 changes: 0 additions & 2 deletions lib/Sema/CodeSynthesis.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ enum class ImplicitConstructorKind {
Memberwise
};

bool isMemberwiseInitialized(VarDecl *var);

/// Create an implicit struct or class constructor.
///
/// \param decl The struct or class for which a constructor will be created.
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5230,7 +5230,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
if (isa<StructDecl>(decl)) {
for (auto member : decl->getMembers()) {
if (auto var = dyn_cast<VarDecl>(member)) {
if (!isMemberwiseInitialized(var))
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
continue;

validateDecl(var);
Expand Down Expand Up @@ -5303,7 +5303,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
if (backingStorageVars.count(var) > 0)
continue;

if (isMemberwiseInitialized(var)) {
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
// Initialized 'let' properties have storage, but don't get an argument
// to the memberwise initializer since they already have an initial
// value that cannot be overridden.
Expand Down