Skip to content

Commit 96952ea

Browse files
committed
[AST] Distinguish memberwise-initialized properties for storage vs. declared
The determination of whether a property is memberwise-initialized is somewhat confused for properties that have synthesized backing properties. Some clients (Sema/Index) want to see the declared properties, while others (SILGen) want to see the backing stored properties. Add a flag to `VarDecl::isMemberwiseInitialized()` to capture this variation.
1 parent 1a169b9 commit 96952ea

File tree

6 files changed

+56
-18
lines changed

6 files changed

+56
-18
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5145,7 +5145,13 @@ class VarDecl : public AbstractStorageDecl {
51455145

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

51505156
// Implement isa/cast/dyncast/etc.
51515157
static bool classof(const Decl *D) {

lib/AST/Decl.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5254,19 +5254,50 @@ bool VarDecl::isSelfParameter() const {
52545254
return false;
52555255
}
52565256

5257-
bool VarDecl::isMemberwiseInitialized() const {
5258-
if (!getDeclContext()->isTypeContext())
5257+
/// Whether the given variable is the backing storage property for
5258+
/// a declared property that is either `lazy` or has an attached
5259+
/// property delegate.
5260+
static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
5261+
if (var->getOriginalDelegatedProperty())
5262+
return true;
5263+
5264+
auto name = var->getName();
5265+
if (name.empty())
52595266
return false;
52605267

5261-
// Implicit, computed, and static properties are not initialized.
5262-
// The exception is lazy properties, which due to batch mode we may or
5263-
// may not have yet finalized, so they may currently be "stored" or
5264-
// "computed" in the current AST state.
5265-
if (isImplicit() || isStatic())
5268+
return name.str().startswith("$__lazy_storage_$_");
5269+
}
5270+
5271+
/// Whether the given variable
5272+
static bool isDeclaredPropertyWithBackingStorage(const VarDecl *var) {
5273+
if (var->getAttrs().hasAttribute<LazyAttr>())
5274+
return true;
5275+
5276+
if (var->getAttachedPropertyDelegate())
5277+
return true;
5278+
5279+
return false;
5280+
}
5281+
5282+
bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
5283+
// Only non-static properties in type context can be part of a memberwise
5284+
// initializer.
5285+
if (!getDeclContext()->isTypeContext() || isStatic())
5286+
return false;
5287+
5288+
// If this is a stored property, and not a backing property in a case where
5289+
// we only want to see the declared properties, it can be memberwise
5290+
// initialized.
5291+
if (hasStorage() && preferDeclaredProperties &&
5292+
isBackingStorageForDeclaredProperty(this))
52665293
return false;
52675294

5268-
if (!hasStorage() && !getAttrs().hasAttribute<LazyAttr>() &&
5269-
!getAttachedPropertyDelegate())
5295+
// If this is a computed property, it's not memberwise initialized unless
5296+
// the caller has asked for the declared properties and it is either a
5297+
// `lazy` property or a property with an attached delegate.
5298+
if (!hasStorage() &&
5299+
!(preferDeclaredProperties &&
5300+
isDeclaredPropertyWithBackingStorage(this)))
52705301
return false;
52715302

52725303
// Initialized 'let' properties have storage, but don't get an argument

lib/Index/Index.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,12 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
360360
return;
361361

362362
unsigned CurLabel = 0;
363-
for (auto Prop : TypeContext->getStoredProperties()) {
364-
if (auto Original = Prop->getOriginalDelegatedProperty())
365-
Prop = Original;
363+
for (auto Member : TypeContext->getMembers()) {
364+
auto Prop = dyn_cast<VarDecl>(Member);
365+
if (!Prop)
366+
continue;
366367

367-
if (!Prop->isMemberwiseInitialized())
368+
if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
368369
continue;
369370

370371
if (CurLabel == LabelLocs.size())

lib/SILGen/SILGenConstructor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
218218
SILValue v;
219219

220220
// If it's memberwise initialized, do so now.
221-
if (field->isMemberwiseInitialized()) {
221+
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) {
222222
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
223223
assert(elti != eltEnd && "number of args does not match number of fields");
224224
(void)eltEnd;

lib/Sema/CodeSynthesis.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2077,7 +2077,7 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
20772077
if (!var)
20782078
continue;
20792079

2080-
if (!var->isMemberwiseInitialized())
2080+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
20812081
continue;
20822082

20832083
accessLevel = std::min(accessLevel, var->getFormalAccess());

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5230,7 +5230,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52305230
if (isa<StructDecl>(decl)) {
52315231
for (auto member : decl->getMembers()) {
52325232
if (auto var = dyn_cast<VarDecl>(member)) {
5233-
if (!var->isMemberwiseInitialized())
5233+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
52345234
continue;
52355235

52365236
validateDecl(var);
@@ -5303,7 +5303,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
53035303
if (backingStorageVars.count(var) > 0)
53045304
continue;
53055305

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

0 commit comments

Comments
 (0)