Skip to content

Commit ff208b6

Browse files
authored
Merge pull request #24363 from DougGregor/is-memberwise-initialized
2 parents 913a1f5 + 96952ea commit ff208b6

File tree

8 files changed

+84
-42
lines changed

8 files changed

+84
-42
lines changed

include/swift/AST/Decl.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5150,7 +5150,17 @@ class VarDecl : public AbstractStorageDecl {
51505150

51515151
/// Returns true if the name is the self identifier and is implicit.
51525152
bool isSelfParameter() const;
5153-
5153+
5154+
/// Determine whether this property will be part of the implicit memberwise
5155+
/// initializer.
5156+
///
5157+
/// \param preferDeclaredProperties When encountering a `lazy` property
5158+
/// or a property that has an attached property delegate, prefer the
5159+
/// actual declared property (which may or may not be considered "stored"
5160+
/// as the moment) to the backing storage property. Otherwise, the stored
5161+
/// backing property will be treated as the member-initialized property.
5162+
bool isMemberwiseInitialized(bool preferDeclaredProperties) const;
5163+
51545164
// Implement isa/cast/dyncast/etc.
51555165
static bool classof(const Decl *D) {
51565166
return D->getKind() == DeclKind::Var || D->getKind() == DeclKind::Param;

include/swift/Sema/IDETypeChecking.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,6 @@ namespace swift {
239239
/// \param DC The DeclContext from which the subscript is being referenced.
240240
Optional<Type> getRootTypeOfKeypathDynamicMember(SubscriptDecl *subscript,
241241
const DeclContext *DC);
242-
243-
/// Determine whether the given property is part of the memberwise initializer
244-
/// for a struct.
245-
bool isMemberwiseInitialized(VarDecl *var);
246242
}
247243

248244
#endif

lib/AST/Decl.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5255,6 +5255,61 @@ bool VarDecl::isSelfParameter() const {
52555255
return false;
52565256
}
52575257

5258+
/// Whether the given variable is the backing storage property for
5259+
/// a declared property that is either `lazy` or has an attached
5260+
/// property delegate.
5261+
static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
5262+
if (var->getOriginalDelegatedProperty())
5263+
return true;
5264+
5265+
auto name = var->getName();
5266+
if (name.empty())
5267+
return false;
5268+
5269+
return name.str().startswith("$__lazy_storage_$_");
5270+
}
5271+
5272+
/// Whether the given variable
5273+
static bool isDeclaredPropertyWithBackingStorage(const VarDecl *var) {
5274+
if (var->getAttrs().hasAttribute<LazyAttr>())
5275+
return true;
5276+
5277+
if (var->getAttachedPropertyDelegate())
5278+
return true;
5279+
5280+
return false;
5281+
}
5282+
5283+
bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
5284+
// Only non-static properties in type context can be part of a memberwise
5285+
// initializer.
5286+
if (!getDeclContext()->isTypeContext() || isStatic())
5287+
return false;
5288+
5289+
// If this is a stored property, and not a backing property in a case where
5290+
// we only want to see the declared properties, it can be memberwise
5291+
// initialized.
5292+
if (hasStorage() && preferDeclaredProperties &&
5293+
isBackingStorageForDeclaredProperty(this))
5294+
return false;
5295+
5296+
// If this is a computed property, it's not memberwise initialized unless
5297+
// the caller has asked for the declared properties and it is either a
5298+
// `lazy` property or a property with an attached delegate.
5299+
if (!hasStorage() &&
5300+
!(preferDeclaredProperties &&
5301+
isDeclaredPropertyWithBackingStorage(this)))
5302+
return false;
5303+
5304+
// Initialized 'let' properties have storage, but don't get an argument
5305+
// to the memberwise initializer since they already have an initial
5306+
// value that cannot be overridden.
5307+
if (isLet() && isParentInitialized())
5308+
return false;
5309+
5310+
return true;
5311+
}
5312+
52585313
void VarDecl::setSpecifier(Specifier specifier) {
52595314
Bits.VarDecl.Specifier = static_cast<unsigned>(specifier);
52605315
setSupportsMutationIfStillStored(

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 (!isMemberwiseInitialized(Prop))
368+
if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
368369
continue;
369370

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

lib/SILGen/SILGenConstructor.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,8 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
217217
auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M);
218218
SILValue v;
219219

220-
// An initialized 'let' property has a single value specified by the
221-
// initializer - it doesn't come from an argument.
222-
if (!field->isStatic() && field->isLet() && field->getParentInitializer()) {
223-
// Cleanup after this initialization.
224-
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
225-
v = SGF.emitRValue(field->getParentInitializer())
226-
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
227-
} else {
220+
// If it's memberwise initialized, do so now.
221+
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) {
228222
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
229223
assert(elti != eltEnd && "number of args does not match number of fields");
230224
(void)eltEnd;
@@ -237,6 +231,14 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
237231
v = std::move(*elti).forwardAsSingleStorageValue(SGF, fieldTy, Loc);
238232
}
239233
++elti;
234+
} else {
235+
// Otherwise, use its initializer.
236+
assert(field->isParentInitialized());
237+
238+
// Cleanup after this initialization.
239+
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
240+
v = SGF.emitRValue(field->getParentInitializer())
241+
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
240242
}
241243

242244
eltValues.push_back(v);

lib/Sema/CodeSynthesis.cpp

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,26 +2052,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
20522052
arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty);
20532053
}
20542054

2055-
bool swift::isMemberwiseInitialized(VarDecl *var) {
2056-
// Implicit, computed, and static properties are not initialized.
2057-
// The exception is lazy properties, which due to batch mode we may or
2058-
// may not have yet finalized, so they may currently be "stored" or
2059-
// "computed" in the current AST state.
2060-
if (var->isImplicit() || var->isStatic())
2061-
return false;
2062-
2063-
if (!var->hasStorage() && !var->getAttrs().hasAttribute<LazyAttr>() &&
2064-
!var->getAttachedPropertyDelegate())
2065-
return false;
2066-
2067-
// Initialized 'let' properties have storage, but don't get an argument
2068-
// to the memberwise initializer since they already have an initial
2069-
// value that cannot be overridden.
2070-
if (var->isLet() && var->isParentInitialized())
2071-
return false;
2072-
2073-
return true;
2074-
}
20752055
/// Create an implicit struct or class constructor.
20762056
///
20772057
/// \param decl The struct or class for which a constructor will be created.
@@ -2099,7 +2079,7 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
20992079
if (!var)
21002080
continue;
21012081

2102-
if (!isMemberwiseInitialized(var))
2082+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
21032083
continue;
21042084

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

lib/Sema/CodeSynthesis.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ enum class ImplicitConstructorKind {
6969
Memberwise
7070
};
7171

72-
bool isMemberwiseInitialized(VarDecl *var);
73-
7472
/// Create an implicit struct or class constructor.
7573
///
7674
/// \param decl The struct or class for which a constructor will be created.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5217,7 +5217,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52175217
if (isa<StructDecl>(decl)) {
52185218
for (auto member : decl->getMembers()) {
52195219
if (auto var = dyn_cast<VarDecl>(member)) {
5220-
if (!isMemberwiseInitialized(var))
5220+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
52215221
continue;
52225222

52235223
validateDecl(var);
@@ -5290,7 +5290,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52905290
if (backingStorageVars.count(var) > 0)
52915291
continue;
52925292

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

0 commit comments

Comments
 (0)