Skip to content

Commit 347e85d

Browse files
committed
[Sema] TypeWrappers: Add unamanged stored properties to the synthesized memberwise init
If a stored property would be in a default memberwise initializer it would be added to the `init` synthesized for a type wrapped type as well i.e. a `let` property without a default.
1 parent 5a73b48 commit 347e85d

File tree

5 files changed

+148
-12
lines changed

5 files changed

+148
-12
lines changed

Diff for: lib/Sema/CodeSynthesis.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,27 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
377377

378378
for (auto *member : decl->getMembers()) {
379379
auto *var = dyn_cast<VarDecl>(member);
380-
if (!(var && var->isAccessedViaTypeWrapper()))
380+
if (!var)
381381
continue;
382382

383+
if (!var->isAccessedViaTypeWrapper()) {
384+
// $_storage itself.
385+
if (var->getName() == ctx.Id_TypeWrapperProperty)
386+
continue;
387+
388+
// Computed properties are not included.
389+
if (!var->hasStorage())
390+
continue;
391+
392+
// If this is a memberwise initializeable property include
393+
// it into the type wrapper initializer otherwise the instance
394+
// of type wrapped type wouldn't be completely initialized.
395+
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
396+
params.push_back(createMemberwiseInitParameter(decl, Loc, var));
397+
398+
continue;
399+
}
400+
383401
Identifier argName = var->getName();
384402
Identifier paramName = argName;
385403

Diff for: lib/Sema/TypeCheckTypeWrapper.cpp

+42-11
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ bool IsPropertyAccessedViaTypeWrapper::evaluate(Evaluator &evaluator,
319319
return false;
320320

321321
// `lazy` properties are not wrapped.
322-
if (property->getAttrs().hasAttribute<LazyAttr>())
322+
if (property->getAttrs().hasAttribute<LazyAttr>() ||
323+
property->isLazyStorageProperty())
323324
return false;
324325

325326
// Properties with attached property wrappers are not considered
@@ -386,11 +387,42 @@ SynthesizeTypeWrapperInitializerBody::evaluate(Evaluator &evaluator,
386387
/*Loc=*/DeclNameLoc(), /*Implicit=*/true),
387388
typeWrapperVar->getName());
388389

389-
SmallVector<Argument, 4> arguments;
390+
// Check whether given parameter requires a direct assignment to
391+
// intialize the property.
392+
//
393+
// If `$Storage` doesn't have a member that corresponds
394+
// to the current parameter it means that this is a property
395+
// that not managed by the type wrapper which has to be
396+
// initialized by direct assignment: `self.<name> = <arg>`
397+
auto useDirectAssignment = [&](ParamDecl *param) {
398+
// Properties with property wrappers are always managed by the type wrapper
399+
if (param->hasAttachedPropertyWrapper())
400+
return false;
401+
return storageType->lookupDirect(param->getName()).empty();
402+
};
403+
404+
SmallVector<ASTNode, 2> body;
405+
406+
SmallVector<Argument, 4> initArgs;
390407
{
391408
for (auto *param : *ctor->getParameters()) {
392409
VarDecl *arg = param;
393410

411+
if (useDirectAssignment(param)) {
412+
auto *propRef = UnresolvedDotExpr::createImplicit(
413+
ctx,
414+
new (ctx) DeclRefExpr({ctor->getImplicitSelfDecl()},
415+
/*Loc=*/DeclNameLoc(), /*Implicit=*/true),
416+
arg->getName());
417+
418+
body.push_back(new (ctx) AssignExpr(
419+
propRef, /*EqualLoc=*/SourceLoc(),
420+
new (ctx) DeclRefExpr({arg}, /*DeclNameLoc=*/DeclNameLoc(),
421+
/*Implicit=*/true),
422+
/*Implicit=*/true));
423+
continue;
424+
}
425+
394426
// type wrappers wrap only backing storage of a wrapped
395427
// property, so in this case we need to pass `_<name>` to
396428
// `$Storage` constructor.
@@ -399,9 +431,9 @@ SynthesizeTypeWrapperInitializerBody::evaluate(Evaluator &evaluator,
399431
(void)param->getPropertyWrapperBackingPropertyType();
400432
}
401433

402-
arguments.push_back({/*labelLoc=*/SourceLoc(), arg->getName(),
403-
new (ctx) DeclRefExpr(arg, /*Loc=*/DeclNameLoc(),
404-
/*Implicit=*/true)});
434+
initArgs.push_back({/*labelLoc=*/SourceLoc(), arg->getName(),
435+
new (ctx) DeclRefExpr(arg, /*Loc=*/DeclNameLoc(),
436+
/*Implicit=*/true)});
405437
}
406438
}
407439

@@ -410,7 +442,7 @@ SynthesizeTypeWrapperInitializerBody::evaluate(Evaluator &evaluator,
410442
TypeExpr::createImplicitForDecl(
411443
/*Loc=*/DeclNameLoc(), storageType, ctor,
412444
ctor->mapTypeIntoContext(storageType->getInterfaceType())),
413-
ArgumentList::createImplicit(ctx, arguments));
445+
ArgumentList::createImplicit(ctx, initArgs));
414446

415447
auto *initRef = new (ctx) UnresolvedMemberExpr(
416448
/*dotLoc=*/SourceLoc(), /*declNameLoc=*/DeclNameLoc(),
@@ -422,11 +454,10 @@ SynthesizeTypeWrapperInitializerBody::evaluate(Evaluator &evaluator,
422454
ctx, initRef,
423455
ArgumentList::forImplicitSingle(ctx, ctx.Id_memberwise, storageInit));
424456

425-
auto *assignment = new (ctx)
426-
AssignExpr(storageVarRef, /*EqualLoc=*/SourceLoc(), typeWrapperInit,
427-
/*Implicit=*/true);
457+
body.push_back(new (ctx) AssignExpr(storageVarRef, /*EqualLoc=*/SourceLoc(),
458+
typeWrapperInit,
459+
/*Implicit=*/true));
428460

429-
return BraceStmt::create(ctx, /*lbloc=*/ctor->getLoc(),
430-
/*body=*/{assignment},
461+
return BraceStmt::create(ctx, /*lbloc=*/ctor->getLoc(), body,
431462
/*rbloc=*/ctor->getLoc(), /*implicit=*/true);
432463
}

Diff for: test/Interpreter/Inputs/type_wrapper_defs.swift

+15
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,18 @@ public struct ComplexPropWrapperTest {
121121
@PropWrapper public var a: [String] = ["a"]
122122
@PropWrapperWithoutInit(value: PropWrapper(wrappedValue: [1, 2, 3])) @PropWrapper public var b: [Int]
123123
}
124+
125+
@Wrapper
126+
public struct PersonWithUnmanagedTest {
127+
public let name: String
128+
129+
public lazy var age: Int = {
130+
30
131+
}()
132+
133+
public var placeOfBirth: String {
134+
get { "Earth" }
135+
}
136+
137+
@PropWrapper public var favoredColor: String = "red"
138+
}

Diff for: test/Interpreter/type_wrappers.swift

+26
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,29 @@ func testPropertyWrappers() {
349349
}
350350

351351
testPropertyWrappers()
352+
353+
do {
354+
var person = PersonWithUnmanagedTest(name: "Arthur Dent")
355+
// CHECK: Wrapper.init($Storage(_favoredColor: type_wrapper_defs.PropWrapper<Swift.String>(value: "red")))
356+
357+
print(person.name)
358+
// CHECK: Arthur Dent
359+
360+
print(person.age)
361+
// CHECK: 30
362+
363+
print(person.placeOfBirth)
364+
// CHECK: Earth
365+
366+
print(person.favoredColor)
367+
// CHECK: in getter
368+
// CHECK-NEXT: red
369+
370+
person.favoredColor = "yellow"
371+
// CHECK: in getter
372+
// CHECK-NEXT: in setter => PropWrapper<String>(value: "yellow")
373+
374+
print(person.favoredColor)
375+
// CHECK: in getter
376+
// CHECK-NEXT: yellow
377+
}

Diff for: test/type/type_wrapper.swift

+46
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,49 @@ func propertyWrapperTests() {
332332
// expected-error@-2 {{cannot convert value of type 'String' to expected argument type 'Float'}}
333333
}
334334
}
335+
336+
func testDeclarationsWithUnmanagedProperties() {
337+
@NoopWrapper
338+
struct WithLet { // expected-note {{'init(name:age:)' declared here}}
339+
let name: String
340+
var age: Int
341+
}
342+
_ = WithLet(age: 0) // expected-error {{missing argument for parameter 'name' in call}}
343+
_ = WithLet(name: "", age: 0) // Ok
344+
345+
@NoopWrapper
346+
struct WithDefaultedLet {
347+
let name: String = "Arthur Dent"
348+
var age: Int
349+
}
350+
351+
_ = WithDefaultedLet(age: 32) // Ok
352+
_ = WithDefaultedLet(name: "", age: 0) // expected-error {{extra argument 'name' in call}}
353+
354+
@NoopWrapper
355+
struct WithLazy {
356+
lazy var name: String = {
357+
"Arthur Dent"
358+
}()
359+
360+
var age: Int = 30
361+
}
362+
363+
_ = WithLazy() // Ok
364+
_ = WithLazy(name: "") // expected-error {{extra argument 'name' in call}}
365+
_ = WithLazy(name: "", age: 0) // expected-error {{extra argument 'name' in call}}
366+
_ = WithLazy(age: 0) // Ok
367+
368+
@NoopWrapper
369+
struct OnlyLazyLetAndComputed {
370+
let name: String
371+
lazy var age: Int = {
372+
30
373+
}()
374+
var planet: String {
375+
get { "Earth" }
376+
}
377+
}
378+
379+
_ = OnlyLazyLetAndComputed(name: "Arthur Dent") // Ok
380+
}

0 commit comments

Comments
 (0)