Skip to content

Commit f34aa79

Browse files
committed
[Property delegates] Implicit initialization for properties with delegates.
When a property delegate type has a default initializer, use that to implicitly initialize properties that use that delegate and do not have their own initializers. For example, this would allow (e.g.), delegate types like the DelayedImmutable and DelayedMutable examples to drop the explicit initialization, e.g., ``` @DelayedMutable var foo: Int ``` would be implicitly initialized via ``` $foo = DelayedMutable() ``` This is a simplistic implementation that does not yet propertly handle definite initialization. Fixes rdar://problem/50266039.
1 parent 830eebb commit f34aa79

File tree

6 files changed

+152
-22
lines changed

6 files changed

+152
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4332,10 +4332,12 @@ ERROR(property_delegate_ambiguous_value_property, none,
43324332
ERROR(property_delegate_wrong_initial_value_init, none,
43334333
"'init(initialValue:)' parameter type (%0) must be the same as its "
43344334
"'value' property type (%1) or an @autoclosure thereof", (Type, Type))
4335-
ERROR(property_delegate_failable_initial_value_init, none,
4336-
"'init(initialValue:)' cannot be failable", ())
4335+
ERROR(property_delegate_failable_init, none,
4336+
"%0 cannot be failable", (DeclName))
43374337
ERROR(property_delegate_ambiguous_initial_value_init, none,
43384338
"property delegate type %0 has multiple initial-value initializers", (Type))
4339+
ERROR(property_delegate_ambiguous_default_value_init, none,
4340+
"property delegate type %0 has multiple default-value initializers", (Type))
43394341
ERROR(property_delegate_type_requirement_not_accessible,none,
43404342
"%select{private|fileprivate|internal|public|open}0 %1 %2 cannot have "
43414343
"more restrictive access than its enclosing property delegate type %3 "

include/swift/AST/PropertyDelegates.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ struct PropertyDelegateTypeInfo {
3636
VarDecl *valueVar = nullptr;
3737

3838
/// The initializer init(initialValue:) that will be called when the
39-
/// initiqlizing the property delegate type from a value of the property type.
39+
/// initializing the property delegate type from a value of the property type.
4040
///
4141
/// This initializer is optional, but if present will be used for the `=`
4242
/// initialization syntax.
4343
ConstructorDecl *initialValueInit = nullptr;
4444

45+
/// The initializer `init()` that will be called to default-initialize a
46+
/// value with an attached property delegate.
47+
ConstructorDecl *defaultInit = nullptr;
48+
4549
/// The property through which the delegate value ($foo) will be accessed,
4650
/// hiding the underlying storage completely.
4751
///

lib/AST/Decl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,15 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
15801580
if (entry.isInitialized())
15811581
return true;
15821582

1583+
// If it has an attached property delegate that vends an `init()`, use that
1584+
// for default initialization.
1585+
if (auto singleVar = getSingleVar()) {
1586+
if (auto delegateInfo = singleVar->getAttachedPropertyDelegateTypeInfo()) {
1587+
if (delegateInfo.defaultInit)
1588+
return true;
1589+
}
1590+
}
1591+
15831592
if (entry.getPattern()->isNeverDefaultInitializable())
15841593
return false;
15851594

lib/Sema/CodeSynthesis.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,23 @@ static VarDecl *synthesizePropertyDelegateStorageDelegateProperty(
15201520
return property;
15211521
}
15221522

1523+
static void typeCheckSynthesizedDelegateInitializer(
1524+
PatternBindingDecl *pbd, VarDecl *backingVar, PatternBindingDecl *parentPBD,
1525+
Expr *&initializer) {
1526+
DeclContext *dc = pbd->getDeclContext();
1527+
ASTContext &ctx = dc->getASTContext();
1528+
1529+
// Type-check the initialization.
1530+
auto &tc = *static_cast<TypeChecker *>(ctx.getLazyResolver());
1531+
tc.typeCheckExpression(initializer, dc);
1532+
if (auto initializerContext =
1533+
dyn_cast_or_null<Initializer>(
1534+
pbd->getPatternEntryForVarDecl(backingVar).getInitContext())) {
1535+
tc.contextualizeInitializer(initializerContext, initializer);
1536+
}
1537+
tc.checkPropertyDelegateErrorHandling(pbd, initializer);
1538+
}
1539+
15231540
llvm::Expected<PropertyDelegateBackingPropertyInfo>
15241541
PropertyDelegateBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
15251542
VarDecl *var) const {
@@ -1589,14 +1606,6 @@ PropertyDelegateBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
15891606

15901607
tc.typeCheckPatternBinding(parentPBD, patternNumber);
15911608
}
1592-
1593-
Expr *originalInitialValue = nullptr;
1594-
if (Expr *init = parentPBD->getInit(patternNumber)) {
1595-
pbd->setInit(0, init);
1596-
pbd->setInitializerChecked(0);
1597-
originalInitialValue = findOriginalPropertyDelegateInitialValue(var, init);
1598-
}
1599-
16001609
// Mark the backing property as 'final'. There's no sensible way to override.
16011610
if (dc->getSelfClassDecl())
16021611
makeFinal(ctx, backingVar);
@@ -1616,6 +1625,23 @@ PropertyDelegateBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
16161625
std::min(defaultAccess, var->getSetterFormalAccess());
16171626
backingVar->overwriteSetterAccess(setterAccess);
16181627

1628+
Expr *originalInitialValue = nullptr;
1629+
if (Expr *init = parentPBD->getInit(patternNumber)) {
1630+
pbd->setInit(0, init);
1631+
pbd->setInitializerChecked(0);
1632+
originalInitialValue = findOriginalPropertyDelegateInitialValue(var, init);
1633+
} else if (!parentPBD->isInitialized(patternNumber) &&
1634+
delegateInfo.defaultInit) {
1635+
// FIXME: Record this expression somewhere so that DI can perform the
1636+
// initialization itself.
1637+
auto typeExpr = TypeExpr::createImplicit(storageType, ctx);
1638+
Expr *initializer = CallExpr::createImplicit(ctx, typeExpr, {}, { });
1639+
typeCheckSynthesizedDelegateInitializer(pbd, backingVar, parentPBD,
1640+
initializer);
1641+
pbd->setInit(0, initializer);
1642+
pbd->setInitializerChecked(0);
1643+
}
1644+
16191645
// If there is a storage delegate property (delegateVar) in the delegate,
16201646
// synthesize a computed property for '$foo'.
16211647
VarDecl *storageVar = nullptr;
@@ -1640,16 +1666,9 @@ PropertyDelegateBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
16401666
Expr *initializer =
16411667
CallExpr::createImplicit(ctx, typeExpr, {origValue},
16421668
{ctx.Id_initialValue});
1669+
typeCheckSynthesizedDelegateInitializer(pbd, backingVar, parentPBD,
1670+
initializer);
16431671

1644-
// Type-check the initialization.
1645-
auto &tc = *static_cast<TypeChecker *>(ctx.getLazyResolver());
1646-
tc.typeCheckExpression(initializer, dc);
1647-
if (auto initializerContext =
1648-
dyn_cast_or_null<Initializer>(
1649-
pbd->getPatternEntryForVarDecl(backingVar).getInitContext())) {
1650-
tc.contextualizeInitializer(initializerContext, initializer);
1651-
}
1652-
tc.checkPropertyDelegateErrorHandling(parentPBD, initializer);
16531672
return PropertyDelegateBackingPropertyInfo(
16541673
backingVar, storageVar, originalInitialValue, initializer, origValue);
16551674
}

lib/Sema/TypeCheckPropertyDelegate.cpp

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,61 @@ static ConstructorDecl *findInitialValueInit(ASTContext &ctx,
160160

161161
// The initializer must not be failable.
162162
if (init->getFailability() != OTK_None) {
163-
init->diagnose(diag::property_delegate_failable_initial_value_init);
163+
init->diagnose(diag::property_delegate_failable_init, initName);
164+
return nullptr;
165+
}
166+
167+
return init;
168+
}
169+
170+
/// Determine whether we have a suitable init() within a property
171+
/// delegate type.
172+
static ConstructorDecl *findDefaultInit(ASTContext &ctx,
173+
NominalTypeDecl *nominal) {
174+
SmallVector<ConstructorDecl *, 2> defaultValueInitializers;
175+
DeclName initName(ctx, DeclBaseName::createConstructor(),
176+
ArrayRef<Identifier>());
177+
SmallVector<ValueDecl *, 2> decls;
178+
nominal->lookupQualified(nominal, initName, NL_QualifiedDefault, decls);
179+
for (const auto &decl : decls) {
180+
auto init = dyn_cast<ConstructorDecl>(decl);
181+
if (!init || init->getDeclContext() != nominal)
182+
continue;
183+
184+
defaultValueInitializers.push_back(init);
185+
}
186+
187+
switch (defaultValueInitializers.size()) {
188+
case 0:
189+
return nullptr;
190+
191+
case 1:
192+
break;
193+
194+
default:
195+
// Diagnose ambiguous init() initializers.
196+
nominal->diagnose(diag::property_delegate_ambiguous_default_value_init,
197+
nominal->getDeclaredType());
198+
for (auto init : defaultValueInitializers) {
199+
init->diagnose(diag::kind_declname_declared_here,
200+
init->getDescriptiveKind(), init->getFullName());
201+
}
202+
return nullptr;
203+
}
204+
205+
// 'init()' must be as accessible as the nominal type.
206+
auto init = defaultValueInitializers.front();
207+
if (init->getFormalAccess() < nominal->getFormalAccess()) {
208+
init->diagnose(diag::property_delegate_type_requirement_not_accessible,
209+
init->getFormalAccess(), init->getDescriptiveKind(),
210+
init->getFullName(), nominal->getDeclaredType(),
211+
nominal->getFormalAccess());
212+
return nullptr;
213+
}
214+
215+
// The initializer must not be failable.
216+
if (init->getFailability() != OTK_None) {
217+
init->diagnose(diag::property_delegate_failable_init, initName);
164218
return nullptr;
165219
}
166220

@@ -186,6 +240,7 @@ PropertyDelegateTypeInfoRequest::evaluate(
186240
PropertyDelegateTypeInfo result;
187241
result.valueVar = valueVar;
188242
result.initialValueInit = findInitialValueInit(ctx, nominal, valueVar);
243+
result.defaultInit = findDefaultInit(ctx, nominal);
189244
result.delegateValueVar =
190245
findValueProperty(ctx, nominal, ctx.Id_delegateValue, /*allowMissing=*/true);
191246

test/SILOptimizer/di-property-delegates.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,50 @@ func testGenericClass() {
304304
}
305305
}
306306

307+
@_propertyDelegate
308+
struct WrapperWithDefaultInit<Value> {
309+
private var _value: Value? = nil
310+
311+
init() {
312+
print("default init called on \(Value.self)")
313+
}
314+
315+
var value: Value {
316+
get {
317+
return _value!
318+
} set {
319+
print("set value \(newValue)")
320+
_value = newValue
321+
}
322+
}
323+
}
324+
325+
struct UseWrapperWithDefaultInit {
326+
@WrapperWithDefaultInit<Int>
327+
var x: Int
328+
329+
@WrapperWithDefaultInit<String>
330+
var y: String
331+
332+
init(y: String) {
333+
self.y = y
334+
}
335+
}
336+
337+
func testDefaultInit() {
338+
// CHECK: ## DefaultInit
339+
print("\n## DefaultInit")
340+
341+
let use = UseWrapperWithDefaultInit(y: "hello")
342+
// CHECK: default init called on Int
343+
344+
// FIXME: DI should eliminate the following call
345+
// CHECK: default init called on String
346+
// CHECK: set value hello
347+
}
307348

308349
testIntStruct()
309350
testIntClass()
310351
testRefStruct()
311352
testGenericClass()
312-
353+
testDefaultInit()

0 commit comments

Comments
 (0)