Skip to content

Commit ee99dc8

Browse files
committed
[SILGen] TypeWrappers: Support default values in user-defined initializers
All of the property initializers are injected as initializations of individual `_storage` fields.
1 parent 7e0cca9 commit ee99dc8

File tree

5 files changed

+178
-12
lines changed

5 files changed

+178
-12
lines changed

lib/SILGen/SILGenDecl.cpp

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,29 +1335,96 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
13351335
auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx),
13361336
JumpDest::invalid());
13371337

1338-
if (auto *Init = PBD->getExecutableInit(idx)) {
1338+
auto emitInitializer = [&](Expr *initExpr, VarDecl *var, bool forLocalContext,
1339+
InitializationPtr &initialization) {
13391340
// If an initial value expression was specified by the decl, emit it into
13401341
// the initialization.
1341-
FullExpr Scope(Cleanups, CleanupLocation(Init));
1342+
FullExpr Scope(Cleanups, CleanupLocation(initExpr));
13421343

1343-
auto *var = PBD->getSingleVar();
1344-
if (var && var->getDeclContext()->isLocalContext()) {
1344+
if (forLocalContext) {
13451345
if (auto *orig = var->getOriginalWrappedProperty()) {
13461346
auto initInfo = orig->getPropertyWrapperInitializerInfo();
13471347
if (auto *placeholder = initInfo.getWrappedValuePlaceholder()) {
1348-
Init = placeholder->getOriginalWrappedValue();
1348+
initExpr = placeholder->getOriginalWrappedValue();
13491349

1350-
auto value = emitRValue(Init);
1351-
emitApplyOfPropertyWrapperBackingInitializer(SILLocation(PBD), orig,
1352-
getForwardingSubstitutionMap(),
1353-
std::move(value))
1354-
.forwardInto(*this, SILLocation(PBD), initialization.get());
1350+
auto value = emitRValue(initExpr);
1351+
emitApplyOfPropertyWrapperBackingInitializer(
1352+
PBD, orig, getForwardingSubstitutionMap(), std::move(value))
1353+
.forwardInto(*this, SILLocation(PBD), initialization.get());
13551354
return;
13561355
}
13571356
}
13581357
}
13591358

1360-
emitExprInto(Init, initialization.get(), SILLocation(PBD));
1359+
emitExprInto(initExpr, initialization.get(), SILLocation(PBD));
1360+
};
1361+
1362+
auto *singleVar = PBD->getSingleVar();
1363+
if (auto *Init = PBD->getExecutableInit(idx)) {
1364+
// If an initial value expression was specified by the decl, emit it into
1365+
// the initialization.
1366+
bool isLocalVar =
1367+
singleVar && singleVar->getDeclContext()->isLocalContext();
1368+
emitInitializer(Init, singleVar, isLocalVar, initialization);
1369+
} else if (singleVar &&
1370+
singleVar->isTypeWrapperLocalStorageForInitializer()) {
1371+
// If any of the type wrapper managed properties had default initializers
1372+
// we need to emit them as assignments to `_storage` elements as part
1373+
// of its initialization.
1374+
1375+
auto storageVarType = singleVar->getType()->castTo<TupleType>();
1376+
auto *wrappedDecl = cast<NominalTypeDecl>(
1377+
singleVar->getDeclContext()->getInnermostTypeContext());
1378+
1379+
SmallVector<std::pair<VarDecl *, Expr *>, 2> fieldsToInitialize;
1380+
fieldsToInitialize.resize_for_overwrite(storageVarType->getNumElements());
1381+
1382+
unsigned numInitializable = 0;
1383+
for (auto member : wrappedDecl->getMembers()) {
1384+
auto *PBD = dyn_cast<PatternBindingDecl>(member);
1385+
// Check every member that is managed by the type wrapper.
1386+
if (!(PBD && PBD->getSingleVar() &&
1387+
PBD->getSingleVar()->isAccessedViaTypeWrapper()))
1388+
continue;
1389+
1390+
auto *field = PBD->getSingleVar();
1391+
auto fieldNo = storageVarType->getNamedElementId(field->getName());
1392+
1393+
if (auto *initExpr = PBD->getInit(/*index=*/0)) {
1394+
fieldsToInitialize[fieldNo] = {PBD->getSingleVar(), initExpr};
1395+
++numInitializable;
1396+
}
1397+
}
1398+
1399+
if (numInitializable == 0) {
1400+
initialization->finishUninitialized(*this);
1401+
return;
1402+
}
1403+
1404+
// If there are any initializable fields, let's split _storage into
1405+
// element initializers and emit initializations for individual fields.
1406+
1407+
assert(initialization->canSplitIntoTupleElements());
1408+
1409+
SmallVector<InitializationPtr, 4> scratch;
1410+
auto fieldInits = initialization->splitIntoTupleElements(
1411+
*this, PBD, storageVarType->getCanonicalType(), scratch);
1412+
1413+
for (unsigned i : range(fieldInits.size())) {
1414+
VarDecl *field;
1415+
Expr *initExpr;
1416+
1417+
std::tie(field, initExpr) = fieldsToInitialize[i];
1418+
1419+
auto &fieldInit = fieldInits[i];
1420+
if (initExpr) {
1421+
emitInitializer(initExpr, field, /*forLocalContext=*/true, fieldInit);
1422+
} else {
1423+
fieldInit->finishUninitialized(*this);
1424+
}
1425+
}
1426+
1427+
initialization->finishInitialization(*this);
13611428
} else {
13621429
// Otherwise, mark it uninitialized for DI to resolve.
13631430
initialization->finishUninitialized(*this);

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,11 @@ public class UserDefinedInitWithConditionalTest<T> {
282282
@Wrapper
283283
public class ClassWithConvenienceInit<T> {
284284
public var a: T?
285-
public var b: String
285+
public var b: String = ""
286+
287+
public init(aWithoutB: T?) {
288+
self.a = aWithoutB
289+
}
286290

287291
init(a: T?, b: String) {
288292
// Just to test that conditionals work properly
@@ -338,3 +342,31 @@ public struct TypeWithLetProperties<T> {
338342
}
339343
}
340344
}
345+
346+
@Wrapper
347+
public class TypeWithDefaultedLetProperties<T> {
348+
let a: T? = nil
349+
let b: Int = 0
350+
351+
public init() {
352+
print(self.a)
353+
print(self.b)
354+
}
355+
}
356+
357+
@Wrapper
358+
public class TypeWithSomeDefaultedLetProperties<T> {
359+
let a: T
360+
let b: Int? = 0
361+
@PropWrapper var c: String = "<default>"
362+
@PropWrapperWithoutInit(value: [1, ""]) var d: [Any]
363+
364+
public init(a: T) {
365+
self.a = a
366+
self.c = "a"
367+
368+
print(self.a)
369+
print(self.b)
370+
print(self.c)
371+
}
372+
}

test/Interpreter/type_wrappers.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,9 @@ do {
563563
// CHECK-NEXT: in getter
564564
// CHECK-NEXT: <modified>
565565

566+
let test2 = ClassWithConvenienceInit(aWithoutB: [1, ""])
567+
// CHECK: Wrapper.init($Storage(a: Optional([1, ""]), b: ""))
568+
566569
func test<T>(_ v: T) {
567570
let test1 = ClassWithConvenienceInit<(Int, String, T)>()
568571
test1.a = (-1, "ultimate question", v)
@@ -622,4 +625,22 @@ do {
622625
// CHECK-NEXT: Optional([1, 2, 3])
623626
// CHECK-NEXT: in read-only getter
624627
// CHECK-NEXT: 0
628+
629+
let test3 = TypeWithDefaultedLetProperties<[String]>()
630+
// CHECK: Wrapper.init($Storage(a: nil, b: 0))
631+
// CHECK-NEXT: in read-only getter
632+
// CHECK-NEXT: nil
633+
// CHECK-NEXT: in read-only getter
634+
// CHECK-NEXT: 0
635+
636+
let test4 = TypeWithSomeDefaultedLetProperties(a: ["", 1.0])
637+
// CHECK: Wrapper.init($Storage(a: ["", 1.0], b: Optional(0), _c: type_wrapper_defs.PropWrapper<Swift.String>(value: "<default>"), _d: type_wrapper_defs.PropWrapperWithoutInit<Swift.Array<Any>>(value: [1, ""])))
638+
// CHECK-NEXT: in getter
639+
// CHECK-NEXT: in setter => PropWrapper<String>(value: "a")
640+
// CHECK-NEXT: in read-only getter
641+
// CHECK-NEXT: ["", 1.0]
642+
// CHECK-NEXT: in read-only getter
643+
// CHECK-NEXT: Optional(0)
644+
// CHECK-NEXT: in getter
645+
// CHECK-NEXT: a
625646
}

test/SILOptimizer/type_wrapper_definite_init_diagnostics.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,19 @@ do {
8888
self.a = 0
8989
}
9090
}
91+
92+
@Wrapper
93+
struct ImmutableReassignmentTest {
94+
let x: Int = 42
95+
96+
init(x: Int) {
97+
self.x = x // expected-error {{immutable value 'self.x' may only be initialized once}}
98+
}
99+
100+
init(optX: Int?) {
101+
if let x = optX {
102+
self.x = x // expected-error {{immutable value 'self.x' may only be initialized once}}
103+
}
104+
}
105+
}
91106
}

test/SILOptimizer/type_wrapper_init_transform.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,34 @@ struct TypeWithLetProperties<T> {
414414
}
415415
}
416416
}
417+
418+
@Wrapper
419+
struct TypeWithDefaultedProperties<T> {
420+
let a: [String]
421+
let b: T? = nil
422+
var c: Int = 42
423+
424+
// CHECK-LABEL: sil hidden [ossa] @$s4test27TypeWithDefaultedPropertiesV1aACyxGSaySSG_tcfC
425+
// --> Defaults handling
426+
// CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(a: Array<String>, b: Optional<T>, c: Int), var, name "_storage", implicit
427+
// CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array<String>, b: Optional<T>, c: Int), 0
428+
// CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array<String>, b: Optional<T>, c: Int), 1
429+
// CHECK-NEXT: [[C_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array<String>, b: Optional<T>, c: Int), 2
430+
// CHECK-NEXT: inject_enum_addr [[B_REF]] : $*Optional<T>, #Optional.none!enumelt
431+
// CHECK-NEXT: [[C_DEFAULT_VAL:%.*]] = integer_literal $Builtin.IntLiteral, 42
432+
// CHECK: [[INT_INIT_REF:%.*]] = function_ref @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int
433+
// CHECK-NEXT: [[C_VAL:%.*]] = apply [[INT_INIT_REF]]([[C_DEFAULT_VAL]], {{.*}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int
434+
// CHECK-NEXT: store [[C_VAL:%.*]] to [trivial] [[C_REF]] : $*Int
435+
// --> Assignment to `let a`
436+
// CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [modify] [static] [[LOCAL_STORAGE]] : $*(a: Array<String>, b: Optional<T>, c: Int)
437+
// CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: Array<String>, b: Optional<T>, c: Int), 0
438+
// CHECK-NEXT: assign [[A_ARG:%.*]] to [init] %18 : $*Array<String>
439+
// CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]]
440+
// --> Assignment to `var c`, note that the assignment is done to a wrapped value
441+
// CHECK: [[C_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array<String>, b: Optional<T>, c: Int), 2
442+
// CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $Int to [assign_wrapped_value] [[C_REF]] : $*Int, set {{.*}}
443+
init(a: [String]) {
444+
self.a = a
445+
self.c = 0
446+
}
447+
}

0 commit comments

Comments
 (0)