Skip to content

Commit 5c8c008

Browse files
authored
Merge pull request #61479 from xedin/type-wrappers-init-values-in-user-defined-inits
[SILGen] TypeWrappers: Support default values in user-defined initializers
2 parents 82d78a3 + 7dfad23 commit 5c8c008

File tree

10 files changed

+214
-37
lines changed

10 files changed

+214
-37
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5864,6 +5864,11 @@ class VarDecl : public AbstractStorageDecl {
58645864
/// backing property will be treated as the member-initialized property.
58655865
bool isMemberwiseInitialized(bool preferDeclaredProperties) const;
58665866

5867+
/// Check whether this variable presents a local storage synthesized
5868+
/// by the compiler in a user-defined designated initializer to
5869+
/// support initialization of type wrapper managed properties.
5870+
bool isTypeWrapperLocalStorageForInitializer() const;
5871+
58675872
/// Return the range of semantics attributes attached to this VarDecl.
58685873
auto getSemanticsAttrs() const
58695874
-> decltype(getAttrs().getAttributes<SemanticsAttr>()) {

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6580,6 +6580,14 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
65806580
return true;
65816581
}
65826582

6583+
bool VarDecl::isTypeWrapperLocalStorageForInitializer() const {
6584+
if (auto *ctor =
6585+
dyn_cast_or_null<ConstructorDecl>(getDeclContext()->getAsDecl())) {
6586+
return this == ctor->getLocalTypeWrapperStorageVar();
6587+
}
6588+
return false;
6589+
}
6590+
65836591
bool VarDecl::isLet() const {
65846592
// An awful hack that stabilizes the value of 'isLet' for ParamDecl instances.
65856593
//

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);

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -468,25 +468,30 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element,
468468

469469
/// If the specified value is a 'let' property in an initializer, return true.
470470
bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const {
471-
NullablePtr<NominalTypeDecl> NTD;
472-
473471
// If this is an element of a `_storage` tuple, we need to
474472
// check the `$Storage` to determine whether underlying storage
475473
// backing element is immutable.
476474
if (auto *storageVar = getAsTypeWrapperLocalStorageVar()) {
477475
auto *wrappedType = cast<NominalTypeDecl>(
478476
storageVar->getDeclContext()->getInnermostTypeContext());
479477
assert(wrappedType && "_storage reference without type wrapper");
480-
NTD = wrappedType->getTypeWrapperStorageDecl();
481-
} else {
482-
// If we aren't representing 'self' in a non-delegating initializer, then we
483-
// can't have 'let' properties.
484-
if (!isNonDelegatingInit())
485-
return IsLet;
486-
487-
NTD = MemorySILType.getNominalOrBoundGenericNominal();
478+
479+
auto storageVarType = storageVar->getInterfaceType()->getAs<TupleType>();
480+
assert(Element < storageVarType->getNumElements());
481+
auto propertyName = storageVarType->getElement(Element).getName();
482+
483+
auto *storageDecl = wrappedType->getTypeWrapperStorageDecl();
484+
auto *property = storageDecl->lookupDirect(propertyName).front();
485+
return cast<VarDecl>(property)->isLet();
488486
}
489487

488+
// If we aren't representing 'self' in a non-delegating initializer, then we
489+
// can't have 'let' properties.
490+
if (!isNonDelegatingInit())
491+
return IsLet;
492+
493+
auto NTD = MemorySILType.getNominalOrBoundGenericNominal();
494+
490495
if (!NTD) {
491496
// Otherwise, we miscounted elements?
492497
assert(Element == 0 && "Element count problem");
@@ -496,7 +501,7 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const {
496501
auto &Module = MemoryInst->getModule();
497502

498503
auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction());
499-
for (auto *VD : NTD.get()->getStoredProperties()) {
504+
for (auto *VD : NTD->getStoredProperties()) {
500505
auto FieldType = MemorySILType.getFieldType(VD, Module, expansionContext);
501506
unsigned NumFieldElements =
502507
getElementCountRec(expansionContext, Module, FieldType, false);

lib/Sema/CodeSynthesis.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -214,20 +214,6 @@ static void maybeAddTypeWrapperDefaultArg(ParamDecl *arg, VarDecl *var,
214214
if (!initExpr)
215215
return;
216216

217-
// Type wrapper variables are never initialized directly,
218-
// initialization expression (if any) becomes an default
219-
// argument of the initializer synthesized by the type wrapper.
220-
{
221-
// Since type wrapper is applied to backing property, that's
222-
// the the initializer it subsumes.
223-
if (var->hasAttachedPropertyWrapper()) {
224-
auto *backingVar = var->getPropertyWrapperBackingProperty();
225-
PBD = backingVar->getParentPatternBinding();
226-
}
227-
228-
PBD->setInitializerSubsumed(/*index=*/0);
229-
}
230-
231217
arg->setDefaultExpr(initExpr, PBD->isInitializerChecked(/*index=*/0));
232218
arg->setDefaultArgumentKind(DefaultArgumentKind::Normal);
233219
}

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,13 @@ VarDecl *GetTypeWrapperStorageForProperty::evaluate(Evaluator &evaluator,
227227
auto *storage = wrappedType->getTypeWrapperStorageDecl();
228228
assert(storage);
229229

230+
// Type wrapper variables are never initialized directly,
231+
// initialization expression (if any) becomes an default
232+
// argument of the initializer synthesized by the type wrapper.
233+
if (auto *PBD = property->getParentPatternBinding()) {
234+
PBD->setInitializerSubsumed(/*index=*/0);
235+
}
236+
230237
return injectProperty(storage, property->getName(),
231238
property->getValueInterfaceType(),
232239
property->getIntroducer(), AccessLevel::Internal);

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)