Skip to content

Commit a9588b3

Browse files
authored
Merge pull request #27253 from theblixguy/fix/SR-11477
[PropertyWrappers] Look for constructors with default args when finding default init
2 parents f44077b + 162f86f commit a9588b3

File tree

6 files changed

+207
-103
lines changed

6 files changed

+207
-103
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4533,8 +4533,6 @@ ERROR(property_wrapper_wrong_initial_value_init, none,
45334533
(DeclName, Type, Type))
45344534
ERROR(property_wrapper_failable_init, none,
45354535
"%0 cannot be failable", (DeclName))
4536-
ERROR(property_wrapper_ambiguous_default_value_init, none,
4537-
"property wrapper type %0 has multiple default-value initializers", (Type))
45384536
ERROR(property_wrapper_type_requirement_not_accessible,none,
45394537
"%select{private|fileprivate|internal|public|open}0 %1 %2 cannot have "
45404538
"more restrictive access than its enclosing property wrapper type %3 "

include/swift/AST/PropertyWrappers.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ struct PropertyWrapperTypeInfo {
4343
HasInitialValueInit
4444
} wrappedValueInit = NoWrappedValueInit;
4545

46-
/// The initializer `init()` that will be called to default-initialize a
46+
/// The initializer that will be called to default-initialize a
4747
/// value with an attached property wrapper.
48-
ConstructorDecl *defaultInit = nullptr;
48+
enum {
49+
NoDefaultValueInit = 0,
50+
HasDefaultValueInit
51+
} defaultInit = NoDefaultValueInit;
4952

5053
/// The property through which the projection value ($foo) will be accessed.
5154
///

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 75 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@
2626
#include "swift/AST/TypeCheckRequests.h"
2727
using namespace swift;
2828

29+
/// The kind of property initializer to look for
30+
enum class PropertyWrapperInitKind {
31+
/// An initial-value initializer (i.e. `init(initialValue:)`)
32+
InitialValue,
33+
/// An wrapped-value initializer (i.e. `init(wrappedValue:)`)
34+
WrappedValue,
35+
/// An default-value initializer (i.e. `init()` or `init(defaultArgs...)`)
36+
Default
37+
};
38+
2939
/// Find the named property in a property wrapper to which access will
3040
/// be delegated.
3141
static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal,
@@ -79,52 +89,63 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal,
7989
return var;
8090
}
8191

82-
/// Determine whether we have a suitable wrapped-value initializer within
83-
/// a property wrapper type.
84-
static ConstructorDecl *findInitialValueInit(
85-
ASTContext &ctx,
86-
NominalTypeDecl *nominal,
87-
VarDecl *valueVar,
88-
Identifier argumentLabel) {
89-
// Retrieve the type of the 'value' property.
90-
Type valueVarType = valueVar->getValueInterfaceType();
91-
92+
/// Determine whether we have a suitable initializer within a property wrapper
93+
/// type.
94+
static ConstructorDecl *
95+
findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal,
96+
VarDecl *valueVar, PropertyWrapperInitKind initKind) {
9297
enum class NonViableReason {
9398
Failable,
9499
ParameterTypeMismatch,
95100
Inaccessible,
96101
};
97-
SmallVector<std::tuple<ConstructorDecl*, NonViableReason, Type>, 2> nonviable;
98-
SmallVector<ConstructorDecl *, 2> initialValueInitializers;
99102

103+
SmallVector<std::tuple<ConstructorDecl *, NonViableReason, Type>, 2>
104+
nonviable;
105+
SmallVector<ConstructorDecl *, 2> viableInitializers;
100106
SmallVector<ValueDecl *, 2> decls;
107+
108+
Identifier argumentLabel;
109+
switch (initKind) {
110+
case PropertyWrapperInitKind::InitialValue:
111+
argumentLabel = ctx.Id_initialValue;
112+
break;
113+
case PropertyWrapperInitKind::WrappedValue:
114+
argumentLabel = ctx.Id_wrappedValue;
115+
break;
116+
case PropertyWrapperInitKind::Default:
117+
break;
118+
}
119+
101120
nominal->lookupQualified(nominal, DeclBaseName::createConstructor(),
102121
NL_QualifiedDefault, decls);
103122
for (const auto &decl : decls) {
104123
auto init = dyn_cast<ConstructorDecl>(decl);
105-
if (!init || init->getDeclContext() != nominal || init->getGenericParams())
124+
if (!init || init->getDeclContext() != nominal || init->isGeneric())
106125
continue;
107126

127+
ParamDecl *argumentParam = nullptr;
128+
bool hasExtraneousParam = false;
108129
// Check whether every parameter meets one of the following criteria:
109130
// (1) The parameter has a default argument, or
110131
// (2) The parameter has the given argument label.
111-
ParamDecl *wrappedValueParam = nullptr;
112132
for (auto param : *init->getParameters()) {
113133
// Recognize the first parameter with the requested argument label.
114-
if (param->getArgumentName() == argumentLabel && !wrappedValueParam) {
115-
wrappedValueParam = param;
134+
if (!argumentLabel.empty() && param->getArgumentName() == argumentLabel &&
135+
!argumentParam) {
136+
argumentParam = param;
116137
continue;
117138
}
118139

119140
if (param->isDefaultArgument())
120141
continue;
121142

122-
// Forget we had a match.
123-
wrappedValueParam = nullptr;
143+
// Skip this init as the param doesn't meet the above criteria
144+
hasExtraneousParam = true;
124145
break;
125146
}
126147

127-
if (!wrappedValueParam)
148+
if (hasExtraneousParam)
128149
continue;
129150

130151
// Failable initializers cannot be used.
@@ -141,33 +162,37 @@ static ConstructorDecl *findInitialValueInit(
141162
continue;
142163
}
143164

144-
if (!wrappedValueParam->hasInterfaceType())
145-
continue;
165+
// Additional checks for initial-value and wrapped-value initializers
166+
if (initKind != PropertyWrapperInitKind::Default) {
167+
if (!argumentParam)
168+
continue;
146169

147-
if (wrappedValueParam->isInOut() || wrappedValueParam->isVariadic())
148-
continue;
170+
if (!argumentParam->hasInterfaceType())
171+
continue;
149172

150-
auto paramType = wrappedValueParam->getInterfaceType();
151-
if (wrappedValueParam->isAutoClosure()) {
152-
if (auto *fnType = paramType->getAs<FunctionType>())
153-
paramType = fnType->getResult();
154-
}
173+
if (argumentParam->isInOut() || argumentParam->isVariadic())
174+
continue;
155175

156-
// The parameter type must be the same as the type of `valueVar` or an
157-
// autoclosure thereof.
158-
if (!paramType->isEqual(valueVarType)) {
159-
nonviable.push_back(
160-
std::make_tuple(init, NonViableReason::ParameterTypeMismatch,
161-
paramType));
162-
continue;
176+
auto paramType = argumentParam->getInterfaceType();
177+
if (argumentParam->isAutoClosure()) {
178+
if (auto *fnType = paramType->getAs<FunctionType>())
179+
paramType = fnType->getResult();
180+
}
181+
182+
// The parameter type must be the same as the type of `valueVar` or an
183+
// autoclosure thereof.
184+
if (!paramType->isEqual(valueVar->getValueInterfaceType())) {
185+
nonviable.push_back(std::make_tuple(
186+
init, NonViableReason::ParameterTypeMismatch, paramType));
187+
continue;
188+
}
163189
}
164190

165-
// Check the type
166-
initialValueInitializers.push_back(init);
191+
viableInitializers.push_back(init);
167192
}
168193

169194
// If we found some nonviable candidates but no viable ones, complain.
170-
if (initialValueInitializers.empty() && !nonviable.empty()) {
195+
if (viableInitializers.empty() && !nonviable.empty()) {
171196
for (const auto &candidate : nonviable) {
172197
auto init = std::get<0>(candidate);
173198
auto reason = std::get<1>(candidate);
@@ -187,69 +212,15 @@ static ConstructorDecl *findInitialValueInit(
187212

188213
case NonViableReason::ParameterTypeMismatch:
189214
init->diagnose(diag::property_wrapper_wrong_initial_value_init,
190-
init->getFullName(), paramType, valueVarType);
215+
init->getFullName(), paramType,
216+
valueVar->getValueInterfaceType());
191217
valueVar->diagnose(diag::decl_declared_here, valueVar->getFullName());
192218
break;
193219
}
194220
}
195221
}
196222

197-
return initialValueInitializers.empty() ? nullptr
198-
: initialValueInitializers.front();
199-
}
200-
201-
/// Determine whether we have a suitable init() within a property
202-
/// wrapper type.
203-
static ConstructorDecl *findDefaultInit(ASTContext &ctx,
204-
NominalTypeDecl *nominal) {
205-
SmallVector<ConstructorDecl *, 2> defaultValueInitializers;
206-
DeclName initName(ctx, DeclBaseName::createConstructor(),
207-
ArrayRef<Identifier>());
208-
SmallVector<ValueDecl *, 2> decls;
209-
nominal->lookupQualified(nominal, initName, NL_QualifiedDefault, decls);
210-
for (const auto &decl : decls) {
211-
auto init = dyn_cast<ConstructorDecl>(decl);
212-
if (!init || init->getDeclContext() != nominal)
213-
continue;
214-
215-
defaultValueInitializers.push_back(init);
216-
}
217-
218-
switch (defaultValueInitializers.size()) {
219-
case 0:
220-
return nullptr;
221-
222-
case 1:
223-
break;
224-
225-
default:
226-
// Diagnose ambiguous init() initializers.
227-
nominal->diagnose(diag::property_wrapper_ambiguous_default_value_init,
228-
nominal->getDeclaredType());
229-
for (auto init : defaultValueInitializers) {
230-
init->diagnose(diag::kind_declname_declared_here,
231-
init->getDescriptiveKind(), init->getFullName());
232-
}
233-
return nullptr;
234-
}
235-
236-
// 'init()' must be as accessible as the nominal type.
237-
auto init = defaultValueInitializers.front();
238-
if (init->getFormalAccess() < nominal->getFormalAccess()) {
239-
init->diagnose(diag::property_wrapper_type_requirement_not_accessible,
240-
init->getFormalAccess(), init->getDescriptiveKind(),
241-
init->getFullName(), nominal->getDeclaredType(),
242-
nominal->getFormalAccess());
243-
return nullptr;
244-
}
245-
246-
// The initializer must not be failable.
247-
if (init->isFailable()) {
248-
init->diagnose(diag::property_wrapper_failable_init, initName);
249-
return nullptr;
250-
}
251-
252-
return init;
223+
return viableInitializers.empty() ? nullptr : viableInitializers.front();
253224
}
254225

255226
/// Determine whether we have a suitable static subscript to which we
@@ -335,10 +306,11 @@ PropertyWrapperTypeInfoRequest::evaluate(
335306

336307
PropertyWrapperTypeInfo result;
337308
result.valueVar = valueVar;
338-
if (findInitialValueInit(ctx, nominal, valueVar, ctx.Id_wrappedValue))
309+
if (findSuitableWrapperInit(ctx, nominal, valueVar,
310+
PropertyWrapperInitKind::WrappedValue))
339311
result.wrappedValueInit = PropertyWrapperTypeInfo::HasWrappedValueInit;
340-
else if (auto init = findInitialValueInit(
341-
ctx, nominal, valueVar, ctx.Id_initialValue)) {
312+
else if (auto init = findSuitableWrapperInit(
313+
ctx, nominal, valueVar, PropertyWrapperInitKind::InitialValue)) {
342314
result.wrappedValueInit = PropertyWrapperTypeInfo::HasInitialValueInit;
343315

344316
if (init->getLoc().isValid()) {
@@ -355,7 +327,11 @@ PropertyWrapperTypeInfoRequest::evaluate(
355327
}
356328
}
357329

358-
result.defaultInit = findDefaultInit(ctx, nominal);
330+
if (findSuitableWrapperInit(ctx, nominal, /*valueVar=*/nullptr,
331+
PropertyWrapperInitKind::Default)) {
332+
result.defaultInit = PropertyWrapperTypeInfo::HasDefaultValueInit;
333+
}
334+
359335
result.projectedValueVar =
360336
findValueProperty(ctx, nominal, ctx.Id_projectedValue,
361337
/*allowMissing=*/true);

test/SILOptimizer/di_property_wrappers.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,57 @@ func testComposed() {
422422
// CHECK-NEXT: .. init Wrapper2<Int>(wrappedValue: 17)
423423
}
424424

425+
// SR-11477
426+
427+
@propertyWrapper
428+
struct SR_11477_W {
429+
let name: String
430+
431+
init(name: String = "DefaultParamInit") {
432+
self.name = name
433+
}
434+
435+
var wrappedValue: Int {
436+
get { return 0 }
437+
}
438+
}
439+
440+
@propertyWrapper
441+
struct SR_11477_W1 {
442+
let name: String
443+
444+
init() {
445+
self.name = "Init"
446+
}
447+
448+
init(name: String = "DefaultParamInit") {
449+
self.name = name
450+
}
451+
452+
var wrappedValue: Int {
453+
get { return 0 }
454+
}
455+
}
456+
457+
struct SR_11477_C {
458+
@SR_11477_W var property: Int
459+
@SR_11477_W1 var property1: Int
460+
461+
init() {}
462+
func foo() { print(_property.name) }
463+
func foo1() { print(_property1.name) }
464+
}
465+
466+
func testWrapperInitWithDefaultArg() {
467+
// CHECK: ## InitWithDefaultArg
468+
print("\n## InitWithDefaultArg")
469+
let use = SR_11477_C()
470+
471+
use.foo()
472+
use.foo1()
473+
// CHECK-NEXT: DefaultParamInit
474+
// CHECK-NEXT: Init
475+
}
425476

426477
testIntStruct()
427478
testIntClass()
@@ -431,3 +482,4 @@ testDefaultInit()
431482
testOptIntStruct()
432483
testDefaultNilOptIntStruct()
433484
testComposed()
485+
testWrapperInitWithDefaultArg()

test/SILOptimizer/di_property_wrappers_errors.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,23 @@ struct IntStructWithClassWrapper {
4646
} // expected-error{{return from initializer without initializing all stored properties}}
4747
// expected-note@-1{{'self.wrapped' not initialized}}
4848
}
49+
50+
// SR_11477
51+
52+
@propertyWrapper
53+
struct SR_11477_W {
54+
let name: String
55+
56+
init<T: ExpressibleByIntegerLiteral>(_ value: T = 0) {
57+
self.name = "Init"
58+
}
59+
60+
var wrappedValue: Int {
61+
get { return 0 }
62+
}
63+
}
64+
65+
struct SR_11477_S {
66+
@SR_11477_W var foo: Int
67+
init() {} // expected-error {{return from initializer without initializing all stored properties}} expected-note {{'self.foo' not initialized}}
68+
}

0 commit comments

Comments
 (0)