Skip to content

Commit 0500f35

Browse files
committed
[Sema] TypeWrappers: Synthesize init parameters for property wrapped members
Since properties with property wrappers are not supported, default init synthesis needs to handle them as well by using correct interface type depending on outer wrapper capabilities and setting correct default expression.
1 parent bf2519a commit 0500f35

File tree

5 files changed

+606
-10
lines changed

5 files changed

+606
-10
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,44 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
190190

191191
static void maybeAddTypeWrapperDefaultArg(ParamDecl *arg, VarDecl *var,
192192
ASTContext &ctx) {
193-
assert(var->isAccessedViaTypeWrapper());
193+
assert(var->isAccessedViaTypeWrapper() || var->hasAttachedPropertyWrapper());
194194

195-
if (!var->getParentPattern()->getSingleVar())
195+
if (!(var->getParentPattern() && var->getParentPattern()->getSingleVar()))
196196
return;
197197

198198
auto *PBD = var->getParentPatternBinding();
199199

200-
auto *initExpr = PBD->getInit(/*index=*/0);
200+
Expr *initExpr = nullptr;
201+
202+
if (var->hasAttachedPropertyWrapper()) {
203+
auto initInfo = var->getPropertyWrapperInitializerInfo();
204+
205+
if (initInfo.hasInitFromWrappedValue()) {
206+
initExpr =
207+
initInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue();
208+
}
209+
} else {
210+
initExpr = PBD->getInit(/*index=*/0);
211+
}
212+
201213
if (!initExpr)
202214
return;
203215

204216
// Type wrapper variables are never initialized directly,
205217
// initialization expression (if any) becomes an default
206218
// argument of the initializer synthesized by the type wrapper.
207-
PBD->setInitializerSubsumed(/*index=*/0);
219+
{
220+
// Since type wrapper is applied to backing property, that's
221+
// the the initializer it subsumes.
222+
if (var->hasAttachedPropertyWrapper()) {
223+
auto *backingVar = var->getPropertyWrapperBackingProperty();
224+
PBD = backingVar->getParentPatternBinding();
225+
}
208226

209-
arg->setDefaultExpr(initExpr, /*isTypeChecked=*/false);
227+
PBD->setInitializerSubsumed(/*index=*/0);
228+
}
229+
230+
arg->setDefaultExpr(initExpr, PBD->isInitializerChecked(/*index=*/0));
210231
arg->setDefaultArgumentKind(DefaultArgumentKind::Normal);
211232
}
212233

@@ -354,11 +375,51 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
354375
if (!(var && var->isAccessedViaTypeWrapper()))
355376
continue;
356377

357-
auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, var->getName(), Loc,
358-
var->getName(), decl);
378+
Identifier argName = var->getName();
379+
Identifier paramName = argName;
380+
381+
auto paramInterfaceType = var->getValueInterfaceType();
382+
DeclAttributes attrs;
383+
384+
// If this is a backing storage of a property wrapped property
385+
// let's use wrapped property as a parameter and synthesize
386+
// appropriate property wrapper initialization upon assignment.
387+
if (auto *wrappedVar = var->getOriginalWrappedProperty(
388+
PropertyWrapperSynthesizedPropertyKind::Backing)) {
389+
// If there is `init(wrappedValue:)` or default value for a wrapped
390+
// property we should use wrapped type, otherwise let's proceed with
391+
// wrapper type.
392+
if (wrappedVar->isPropertyMemberwiseInitializedWithWrappedType()) {
393+
var = wrappedVar;
394+
// If parameter have to get wrapped type, let's re-map both argument
395+
// and parameter name to match wrapped property and let property
396+
// wrapper attributes generate wrapped value and projection variables.
397+
argName = wrappedVar->getName();
398+
paramName = argName;
399+
400+
paramInterfaceType = var->getPropertyWrapperInitValueInterfaceType();
401+
// The parameter needs to have all of the property wrapper
402+
// attributes to generate projection and wrapper variables.
403+
for (auto *attr : wrappedVar->getAttachedPropertyWrappers())
404+
attrs.add(attr);
405+
} else {
406+
// If parameter has to have wrapper type then argument type should
407+
// match that of a wrapped property but parameter name stays the same
408+
// since it represents the type of backing storage and could be passed
409+
// to `$Storage` constructor directly.
410+
argName = wrappedVar->getName();
411+
}
412+
}
413+
414+
if (!paramInterfaceType || paramInterfaceType->hasError())
415+
continue;
416+
417+
auto *arg =
418+
new (ctx) ParamDecl(SourceLoc(), Loc, argName, Loc, paramName, decl);
359419

420+
arg->getAttrs().add(attrs);
360421
arg->setSpecifier(ParamSpecifier::Default);
361-
arg->setInterfaceType(var->getValueInterfaceType());
422+
arg->setInterfaceType(paramInterfaceType);
362423
arg->setImplicit();
363424

364425
maybeAddTypeWrapperDefaultArg(arg, var, ctx);

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,18 @@ SynthesizeTypeWrapperInitializerBody::evaluate(Evaluator &evaluator,
389389
SmallVector<Argument, 4> arguments;
390390
{
391391
for (auto *param : *ctor->getParameters()) {
392-
arguments.push_back({/*labelLoc=*/SourceLoc(), param->getName(),
393-
new (ctx) DeclRefExpr(param, /*Loc=*/DeclNameLoc(),
392+
VarDecl *arg = param;
393+
394+
// type wrappers wrap only backing storage of a wrapped
395+
// property, so in this case we need to pass `_<name>` to
396+
// `$Storage` constructor.
397+
if (param->hasAttachedPropertyWrapper()) {
398+
arg = param->getPropertyWrapperBackingProperty();
399+
(void)param->getPropertyWrapperBackingPropertyType();
400+
}
401+
402+
arguments.push_back({/*labelLoc=*/SourceLoc(), arg->getName(),
403+
new (ctx) DeclRefExpr(arg, /*Loc=*/DeclNameLoc(),
394404
/*Implicit=*/true)});
395405
}
396406
}

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,64 @@ public struct Wrapper<S> {
1919
}
2020
}
2121

22+
@propertyWrapper
23+
public struct PropWrapper<Value> {
24+
public var value: Value
25+
26+
public init(wrappedValue: Value) {
27+
self.value = wrappedValue
28+
}
29+
30+
public var projectedValue: Self { return self }
31+
32+
public var wrappedValue: Value {
33+
get {
34+
self.value
35+
}
36+
set {
37+
self.value = newValue
38+
}
39+
}
40+
}
41+
42+
@propertyWrapper
43+
public struct PropWrapperWithoutInit<Value> {
44+
public var value: Value
45+
46+
public init(value: Value) {
47+
self.value = value
48+
}
49+
50+
public var projectedValue: Self { return self }
51+
52+
public var wrappedValue: Value {
53+
get {
54+
self.value
55+
}
56+
set {
57+
self.value = newValue
58+
}
59+
}
60+
}
61+
62+
@propertyWrapper
63+
public struct PropWrapperWithoutProjection<Value> {
64+
public var value: Value
65+
66+
public init(wrappedValue: Value) {
67+
self.value = wrappedValue
68+
}
69+
70+
public var wrappedValue: Value {
71+
get {
72+
self.value
73+
}
74+
set {
75+
self.value = newValue
76+
}
77+
}
78+
}
79+
2280
@Wrapper
2381
public class Person<T> {
2482
public var name: String
@@ -30,3 +88,36 @@ public struct PersonWithDefaults {
3088
public var name: String = "<no name>"
3189
public var age: Int = 99
3290
}
91+
92+
@Wrapper
93+
public struct PropWrapperTest {
94+
@PropWrapper public var test: Int
95+
}
96+
97+
@Wrapper
98+
public struct DefaultedPropWrapperTest {
99+
@PropWrapper public var test: Int = 0
100+
}
101+
102+
@Wrapper
103+
public struct DefaultedPropWrapperWithArgTest {
104+
@PropWrapper(wrappedValue: 3) public var test: Int
105+
}
106+
107+
@Wrapper
108+
public struct PropWrapperNoInitTest {
109+
@PropWrapperWithoutInit public var a: Int
110+
@PropWrapperWithoutInit(value: "b") public var b: String
111+
}
112+
113+
@Wrapper
114+
public struct PropWrapperNoProjectionTest {
115+
@PropWrapperWithoutProjection public var a: Int = 0
116+
@PropWrapperWithoutProjection(wrappedValue: PropWrapper(wrappedValue: "b")) @PropWrapper public var b: String
117+
}
118+
119+
@Wrapper
120+
public struct ComplexPropWrapperTest {
121+
@PropWrapper public var a: [String] = ["a"]
122+
@PropWrapperWithoutInit(value: PropWrapper(wrappedValue: [1, 2, 3])) @PropWrapper public var b: [Int]
123+
}

0 commit comments

Comments
 (0)