Skip to content

Commit a97b4c8

Browse files
authored
Merge pull request #24430 from DougGregor/property-delegates-implicit-init
2 parents d912e33 + 6933382 commit a97b4c8

File tree

7 files changed

+195
-29
lines changed

7 files changed

+195
-29
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: 31 additions & 3 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

@@ -5325,7 +5334,9 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
53255334
origVar = origDelegate;
53265335
if (origVar->getFormalAccess() < AccessLevel::Internal &&
53275336
origVar->getAttachedPropertyDelegate() &&
5328-
origVar->isParentInitialized())
5337+
(origVar->isParentInitialized() ||
5338+
(origVar->getParentPatternBinding() &&
5339+
origVar->getParentPatternBinding()->isDefaultInitializable())))
53295340
return false;
53305341

53315342
return true;
@@ -5736,8 +5747,25 @@ ParamDecl::getDefaultValueStringRepresentation(
57365747
return getASTContext().SourceMgr.extractText(charRange);
57375748
}
57385749

5739-
auto init = findOriginalPropertyDelegateInitialValue(
5740-
original, original->getParentInitializer());
5750+
// If there is no parent initializer, we used the default initializer.
5751+
auto parentInit = original->getParentInitializer();
5752+
if (!parentInit) {
5753+
if (auto type = original->getPropertyDelegateBackingPropertyType()) {
5754+
if (auto nominal = type->getAnyNominal()) {
5755+
scratch.clear();
5756+
auto typeName = nominal->getName().str();
5757+
scratch.append(typeName.begin(), typeName.end());
5758+
scratch.push_back('(');
5759+
scratch.push_back(')');
5760+
return {scratch.data(), scratch.size()};
5761+
}
5762+
}
5763+
5764+
return ".init()";
5765+
}
5766+
5767+
auto init =
5768+
findOriginalPropertyDelegateInitialValue(original, parentInit);
57415769
return extractInlinableText(getASTContext().SourceMgr, init, scratch);
57425770
}
57435771
}

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/IDE/print_property_delegates.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,28 @@
55

66
@_propertyDelegate
77
struct Delegate<Value> {
8-
var value: Value
8+
var _stored: Value?
9+
10+
var value: Value {
11+
get {
12+
return _stored!
13+
}
14+
15+
set {
16+
_stored = newValue
17+
}
18+
}
19+
20+
init() {
21+
self._stored = nil
22+
}
923

1024
init(initialValue: Value) {
11-
self.value = initialValue
25+
self._stored = initialValue
1226
}
1327

1428
init(closure: () -> Value) {
15-
self.value = closure()
29+
self._stored = closure()
1630
}
1731
}
1832

@@ -29,8 +43,11 @@ struct HasDelegates {
2943
@Delegate
3044
var y = true
3145

46+
@Delegate
47+
var z: String
48+
3249
// Memberwise initializer.
33-
// CHECK: init(x: Delegate<Int> = Delegate(closure: foo), y: Bool = true)
50+
// CHECK: init(x: Delegate<Int> = Delegate(closure: foo), y: Bool = true, z: String = Delegate())
3451
}
3552

3653
func trigger() {

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)