Skip to content

Commit 6526cfa

Browse files
committed
Memberwise initializer synthesis for properties with attached delegates.
1 parent b18a290 commit 6526cfa

File tree

7 files changed

+140
-21
lines changed

7 files changed

+140
-21
lines changed

include/swift/AST/Decl.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -2141,11 +2141,7 @@ class PatternBindingDecl final : public Decl,
21412141

21422142
/// Determines whether this binding either has an initializer expression, or is
21432143
/// default initialized, without performing any type checking on it.
2144-
///
2145-
/// This is only valid to check for bindings which have storage.
21462144
bool isDefaultInitializable() const {
2147-
assert(hasStorage());
2148-
21492145
for (unsigned i = 0, e = getNumPatternEntries(); i < e; ++i)
21502146
if (!isDefaultInitializable(i))
21512147
return false;
@@ -4914,12 +4910,19 @@ class VarDecl : public AbstractStorageDecl {
49144910
return nullptr;
49154911
}
49164912

4913+
/// Whether there exists an initializer for this \c VarDecl.
4914+
bool isParentInitialized() const {
4915+
if (auto *PBD = getParentPatternBinding())
4916+
return PBD->getPatternEntryForVarDecl(this).isInitialized();
4917+
return false;
4918+
}
4919+
49174920
// Return whether this VarDecl has an initial value, either by checking
49184921
// if it has an initializer in its parent pattern binding or if it has
49194922
// the @_hasInitialValue attribute.
49204923
bool hasInitialValue() const {
49214924
return getAttrs().hasAttribute<HasInitialValueAttr>() ||
4922-
getParentInitializer();
4925+
isParentInitialized();
49234926
}
49244927

49254928
VarDecl *getOverriddenDecl() const {

lib/AST/Decl.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
15361536
const auto entry = getPatternList()[i];
15371537

15381538
// If it has an initializer expression, this is trivially true.
1539-
if (entry.getInit())
1539+
if (entry.isInitialized())
15401540
return true;
15411541

15421542
if (entry.getPattern()->isNeverDefaultInitializable())
@@ -4922,7 +4922,7 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
49224922

49234923
// If the decl has an explicitly written initializer with a pattern binding,
49244924
// then it isn't settable.
4925-
if (getParentInitializer() != nullptr)
4925+
if (isParentInitialized())
49264926
return false;
49274927

49284928
// Normal lets (e.g. globals) are only mutable in the context of the
@@ -5218,6 +5218,9 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const {
52185218

52195219
CustomAttr *VarDecl::getAttachedPropertyDelegate() const {
52205220
auto &ctx = getASTContext();
5221+
if (!ctx.getLazyResolver())
5222+
return nullptr;
5223+
52215224
auto mutableThis = const_cast<VarDecl *>(this);
52225225
return evaluateOrDefault(ctx.evaluator,
52235226
AttachedPropertyDelegateRequest{mutableThis},
@@ -5241,6 +5244,9 @@ PropertyDelegateTypeInfo VarDecl::getAttachedPropertyDelegateTypeInfo() const {
52415244

52425245
Type VarDecl::getAttachedPropertyDelegateType() const {
52435246
auto &ctx = getASTContext();
5247+
if (!ctx.getLazyResolver())
5248+
return nullptr;
5249+
52445250
auto mutableThis = const_cast<VarDecl *>(this);
52455251
return evaluateOrDefault(ctx.evaluator,
52465252
AttachedPropertyDelegateTypeRequest{mutableThis},
@@ -5249,6 +5255,9 @@ Type VarDecl::getAttachedPropertyDelegateType() const {
52495255

52505256
Type VarDecl::getPropertyDelegateBackingPropertyType() const {
52515257
ASTContext &ctx = getASTContext();
5258+
if (!ctx.getLazyResolver())
5259+
return nullptr;
5260+
52525261
auto mutableThis = const_cast<VarDecl *>(this);
52535262
return evaluateOrDefault(
52545263
ctx.evaluator, PropertyDelegateBackingPropertyTypeRequest{mutableThis},
@@ -5257,6 +5266,9 @@ Type VarDecl::getPropertyDelegateBackingPropertyType() const {
52575266

52585267
VarDecl *VarDecl::getPropertyDelegateBackingProperty() const {
52595268
auto &ctx = getASTContext();
5269+
if (!ctx.getLazyResolver())
5270+
return nullptr;
5271+
52605272
auto mutableThis = const_cast<VarDecl *>(this);
52615273
return evaluateOrDefault(ctx.evaluator,
52625274
PropertyDelegateBackingPropertyRequest{mutableThis},

lib/Sema/CodeSynthesis.cpp

+3-7
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ PropertyDelegateBackingPropertyRequest::evaluate(Evaluator &evaluator,
14641464
VarDecl *backingVar = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(),
14651465
VarDecl::Specifier::Var,
14661466
/*IsCaptureList=*/false,
1467-
SourceLoc(),
1467+
var->getLoc(),
14681468
name, dc);
14691469
backingVar->setInterfaceType(storageInterfaceType);
14701470
backingVar->setType(storageType);
@@ -1856,10 +1856,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
18561856
if (!var->getParentPattern()->getSingleVar())
18571857
return;
18581858

1859-
// If this property has a delegate, don't give it a default argument.
1860-
if (var->getAttachedPropertyDelegate())
1861-
return;
1862-
18631859
// If we don't have an expression initializer or silgen can't assign a default
18641860
// initializer, then we can't generate a default value. An example of where
18651861
// silgen can assign a default is var x: Int? where the default is nil.
@@ -1882,7 +1878,7 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
18821878
// the type will always be a sugared T? because we don't default init an
18831879
// explicit Optional<T>.
18841880
if ((isa<OptionalType>(var->getValueInterfaceType().getPointer()) &&
1885-
!var->getParentInitializer()) ||
1881+
!var->isParentInitialized()) ||
18861882
var->getAttrs().hasAttribute<LazyAttr>()) {
18871883
arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral);
18881884
return;
@@ -1909,7 +1905,7 @@ bool swift::isMemberwiseInitialized(VarDecl *var) {
19091905
// Initialized 'let' properties have storage, but don't get an argument
19101906
// to the memberwise initializer since they already have an initial
19111907
// value that cannot be overridden.
1912-
if (var->isLet() && var->getParentInitializer())
1908+
if (var->isLet() && var->isParentInitialized())
19131909
return false;
19141910

19151911
return true;

lib/Sema/DebuggerTestingTransform.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class DebuggerTestingTransform : public ASTWalker {
177177

178178
// Don't capture variables which aren't default-initialized.
179179
if (auto *VD = dyn_cast<VarDecl>(DstDecl))
180-
if (!VD->getParentInitializer() && !VD->isInOut())
180+
if (!VD->isParentInitialized() && !VD->isInOut())
181181
return {true, OriginalExpr};
182182

183183
// Rewrite the original expression into this:

lib/Sema/DerivedConformanceCodable.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static bool validateCodingKeysEnum(DerivedConformance &derived,
223223
continue;
224224
}
225225

226-
if (varDecl->getParentInitializer())
226+
if (varDecl->isParentInitialized())
227227
continue;
228228

229229
// The var was not default initializable, and did not have an explicit
@@ -870,7 +870,7 @@ static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl, void *) {
870870
lookupVarDeclForCodingKeysCase(conformanceDC, elt, targetDecl);
871871

872872
// Don't output a decode statement for a var let with a default value.
873-
if (varDecl->isLet() && varDecl->getParentInitializer() != nullptr)
873+
if (varDecl->isLet() && varDecl->isParentInitialized())
874874
continue;
875875

876876
auto methodName =

lib/Sema/TypeCheckDecl.cpp

+43-4
Original file line numberDiff line numberDiff line change
@@ -4990,6 +4990,31 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
49904990
}
49914991
}
49924992

4993+
// Lazily construct a mapping from backing storage properties to the
4994+
// declared properties.
4995+
bool computedBackingToOriginalVars = false;
4996+
llvm::SmallDenseMap<VarDecl *, VarDecl *> backingToOriginalVars;
4997+
auto getOriginalVar = [&](VarDecl *var) -> VarDecl * {
4998+
// If we haven't computed the mapping yet, do so now.
4999+
if (!computedBackingToOriginalVars) {
5000+
for (auto member : classDecl->getMembers()) {
5001+
if (auto var = dyn_cast<VarDecl>(member)) {
5002+
if (auto backingVar = var->getPropertyDelegateBackingProperty()) {
5003+
backingToOriginalVars[backingVar] = var;
5004+
}
5005+
}
5006+
}
5007+
5008+
computedBackingToOriginalVars = true;
5009+
}
5010+
5011+
auto known = backingToOriginalVars.find(var);
5012+
if (known == backingToOriginalVars.end())
5013+
return nullptr;
5014+
5015+
return known->second;
5016+
};
5017+
49935018
for (auto member : classDecl->getMembers()) {
49945019
auto pbd = dyn_cast<PatternBindingDecl>(member);
49955020
if (!pbd)
@@ -5000,12 +5025,19 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
50005025
continue;
50015026

50025027
for (auto entry : pbd->getPatternList()) {
5003-
if (entry.getInit()) continue;
5028+
if (entry.isInitialized()) continue;
50045029

50055030
SmallVector<VarDecl *, 4> vars;
50065031
entry.getPattern()->collectVariables(vars);
50075032
if (vars.empty()) continue;
50085033

5034+
// Replace the variables we found with the originals for diagnostic
5035+
// purposes.
5036+
for (auto &var : vars) {
5037+
if (auto originalVar = getOriginalVar(var))
5038+
var = originalVar;
5039+
}
5040+
50095041
auto varLoc = vars[0]->getLoc();
50105042

50115043
Optional<InFlightDiagnostic> diag;
@@ -5235,6 +5267,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52355267
}
52365268

52375269
} else {
5270+
SmallPtrSet<VarDecl *, 4> backingStorageVars;
52385271
for (auto member : decl->getMembers()) {
52395272
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
52405273
// Initializers that were synthesized to fulfill derived conformances
@@ -5259,16 +5292,22 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52595292
if (auto var = dyn_cast<VarDecl>(member)) {
52605293
// If this variable has a property delegate, go validate it to ensure
52615294
// that we create the backing storage property.
5262-
if (var->getAttachedPropertyDelegate()) {
5295+
if (auto backingVar = var->getPropertyDelegateBackingProperty()) {
52635296
validateDecl(var);
52645297
maybeAddAccessorsToStorage(var);
5298+
5299+
backingStorageVars.insert(backingVar);
52655300
}
52665301

5302+
// Ignore the backing storage for properties with attached delegates.
5303+
if (backingStorageVars.count(var) > 0)
5304+
continue;
5305+
52675306
if (isMemberwiseInitialized(var)) {
52685307
// Initialized 'let' properties have storage, but don't get an argument
52695308
// to the memberwise initializer since they already have an initial
52705309
// value that cannot be overridden.
5271-
if (var->isLet() && var->getParentInitializer()) {
5310+
if (var->isLet() && var->isParentInitialized()) {
52725311

52735312
// We cannot handle properties like:
52745313
// let (a,b) = (1,2)
@@ -5295,7 +5334,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
52955334
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
52965335
if (pbd->hasStorage() && !pbd->isStatic()) {
52975336
for (auto entry : pbd->getPatternList()) {
5298-
if (entry.getInit()) continue;
5337+
if (entry.isInitialized()) continue;
52995338

53005339
// If one of the bound variables is @NSManaged, go ahead no matter
53015340
// what.

test/decl/var/property_delegates.swift

+69
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,72 @@ func testBox(ub: UseBox) {
510510
mutableUB.y = 20
511511
mutableUB = ub
512512
}
513+
514+
// ---------------------------------------------------------------------------
515+
// Memberwise initializers
516+
// ---------------------------------------------------------------------------
517+
struct MemberwiseInits<T> {
518+
@Wrapper
519+
var x: Bool
520+
521+
@WrapperWithInitialValue
522+
var y: T
523+
}
524+
525+
func testMemberwiseInits() {
526+
// expected-error@+1{{type '(Wrapper<Bool>, Double) -> MemberwiseInits<Double>'}}
527+
let _: Int = MemberwiseInits<Double>.init
528+
529+
_ = MemberwiseInits(x: Wrapper(value: true), y: 17)
530+
}
531+
532+
struct DefaultedMemberwiseInits {
533+
@Wrapper(value: true)
534+
var x: Bool
535+
536+
@WrapperWithInitialValue
537+
var y: Int = 17
538+
}
539+
540+
func testDefaultedMemberwiseInits() {
541+
_ = DefaultedMemberwiseInits()
542+
_ = DefaultedMemberwiseInits(x: Wrapper(value: false), y: 42)
543+
544+
_ = DefaultedMemberwiseInits(y: 42)
545+
_ = DefaultedMemberwiseInits(x: Wrapper(value: false))
546+
}
547+
548+
// ---------------------------------------------------------------------------
549+
// Default initializers
550+
// ---------------------------------------------------------------------------
551+
struct DefaultInitializerStruct {
552+
@Wrapper(value: true)
553+
var x
554+
555+
@WrapperWithInitialValue
556+
var y: Int = 10
557+
}
558+
559+
struct NoDefaultInitializerStruct { // expected-note{{'init(x:)' declared here}}
560+
@Wrapper
561+
var x: Bool
562+
}
563+
564+
class DefaultInitializerClass {
565+
@Wrapper(value: true)
566+
final var x
567+
568+
@WrapperWithInitialValue
569+
final var y: Int = 10
570+
}
571+
572+
class NoDefaultInitializerClass { // expected-error{{class 'NoDefaultInitializerClass' has no initializers}}
573+
@Wrapper
574+
final var x: Bool // expected-note{{stored property 'x' without initial value prevents synthesized initializers}}
575+
}
576+
577+
func testDefaultInitializers() {
578+
_ = DefaultInitializerStruct()
579+
_ = DefaultInitializerClass()
580+
_ = NoDefaultInitializerStruct() // expected-error{{missing argument for parameter 'x' in call}}
581+
}

0 commit comments

Comments
 (0)