[Sema] TypeWrappers: convert variable init expr into initializer default

All of the stored properties are wrapped which means that their
initializers are subsummed and moved to the synthesized `init`
as default arguments.
This commit is contained in:
Pavel Yaskevich
2022-08-01 16:38:49 -07:00
parent a3b54308d2
commit 39b1566240
4 changed files with 90 additions and 0 deletions

View File

@@ -188,6 +188,28 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty);
}
static void maybeAddTypeWrapperDefaultArg(ParamDecl *arg, VarDecl *var,
ASTContext &ctx) {
assert(var->isAccessedViaTypeWrapper());
if (!var->getParentPattern()->getSingleVar())
return;
auto *PBD = var->getParentPatternBinding();
auto *initExpr = PBD->getInit(/*index=*/0);
if (!initExpr)
return;
// Type wrapper variables are never initialized directly,
// initialization expression (if any) becomes an default
// argument of the initializer synthesized by the type wrapper.
PBD->setInitializerSubsumed(/*index=*/0);
arg->setDefaultExpr(initExpr, /*isTypeChecked=*/false);
arg->setDefaultArgumentKind(DefaultArgumentKind::Normal);
}
/// Describes the kind of implicit constructor that will be
/// generated.
enum class ImplicitConstructorKind {
@@ -339,6 +361,8 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
arg->setInterfaceType(var->getValueInterfaceType());
arg->setImplicit();
maybeAddTypeWrapperDefaultArg(arg, var, ctx);
params.push_back(arg);
}
}

View File

@@ -24,3 +24,9 @@ public class Person<T> {
public var name: String
public var projects: [T]
}
@Wrapper
public struct PersonWithDefaults {
public var name: String = "<no name>"
public var age: Int = 99
}

View File

@@ -37,3 +37,48 @@ print(p.name)
print(p.projects)
// CHECK: in getter
// CHECK-NEXT: ["A", "B", "C", "D"]
var pDefaults = PersonWithDefaults()
// CHECK: Wrapper.init($Storage(name: "<no name>", age: 99))
print(pDefaults.name)
// CHECK: in getter
// CHECK: <no name>
print(pDefaults.age)
// CHECK: in getter
// CHECK: 99
pDefaults.name = "Actual Name"
// CHECK-NEXT: in setter => Actual Name
pDefaults.age = 0
// CHECK-NEXT: in setter => 0
print(pDefaults.name)
// CHECK: in getter
// CHECK: Actual Name
print(pDefaults.age)
// CHECK: in getter
// CHECK: 0
let pDefaultsAge = PersonWithDefaults(name: "Actual Name")
print(pDefaultsAge.name)
// CHECK: in getter
// CHECK: Actual Name
print(pDefaultsAge.age)
// CHECK: in getter
// CHECK: 99
let pDefaultsName = PersonWithDefaults(age: 31337)
print(pDefaultsName.name)
// CHECK: in getter
// CHECK: <no name>
print(pDefaultsName.age)
// CHECK: in getter
// CHECK: 31337

View File

@@ -147,3 +147,18 @@ func testLocalWithNestedWrapper() {
_ = t.test // Ok
_ = t.computed // Ok
}
func testTypeWrapperWithDefaults() {
@NoopWrapper
struct A {
var question: String = "Ultimate Question"
var answer: Int = 42
}
let a = A()
_ = a.question
_ = a.answer
_ = A(question: "")
_ = A(answer: 0)
}