Skip to content

Commit d6b6bbc

Browse files
committed
[DI] TypeWrapper: Adjust immutability check to handle _storage as $Storage
`_storage` is used primary to support member-by-member initialization of `$Storage`, so we need to refer to `$Storage` to determine whether a partial field of `_storage` is `let` or not.
1 parent 8befa76 commit d6b6bbc

File tree

2 files changed

+112
-8
lines changed

2 files changed

+112
-8
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -468,22 +468,35 @@ 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-
// If we aren't representing 'self' in a non-delegating initializer, then we
472-
// can't have 'let' properties.
473-
if (!isNonDelegatingInit())
474-
return IsLet;
475-
476-
auto &Module = MemoryInst->getModule();
471+
NullablePtr<NominalTypeDecl> NTD;
472+
473+
// If this is an element of a `_storage` tuple, we need to
474+
// check the `$Storage` to determine whether underlying storage
475+
// backing element is immutable.
476+
if (auto *storageVar = getAsTypeWrapperLocalStorageVar()) {
477+
auto *wrappedType = cast<NominalTypeDecl>(
478+
storageVar->getDeclContext()->getInnermostTypeContext());
479+
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();
488+
}
477489

478-
auto *NTD = MemorySILType.getNominalOrBoundGenericNominal();
479490
if (!NTD) {
480491
// Otherwise, we miscounted elements?
481492
assert(Element == 0 && "Element count problem");
482493
return false;
483494
}
484495

496+
auto &Module = MemoryInst->getModule();
497+
485498
auto expansionContext = TypeExpansionContext(*MemoryInst->getFunction());
486-
for (auto *VD : NTD->getStoredProperties()) {
499+
for (auto *VD : NTD.get()->getStoredProperties()) {
487500
auto FieldType = MemorySILType.getFieldType(VD, Module, expansionContext);
488501
unsigned NumFieldElements =
489502
getElementCountRec(expansionContext, Module, FieldType, false);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %target-swift-frontend -enable-experimental-feature TypeWrappers -enable-copy-propagation=requested-passes-only -emit-sil -primary-file %s -o /dev/null -verify
2+
3+
// REQUIRES: asserts
4+
5+
@typeWrapper
6+
struct Wrapper<S> {
7+
var underlying: S
8+
9+
init(memberwise: S) { self.underlying = memberwise }
10+
11+
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
12+
get { underlying[keyPath: path] }
13+
}
14+
15+
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V {
16+
get { underlying[keyPath: path] }
17+
set { underlying[keyPath: path] = newValue }
18+
}
19+
}
20+
21+
do {
22+
@Wrapper
23+
struct ImmutableTest1 {
24+
let x: Int
25+
26+
init(x: Int) {
27+
self.x = x
28+
self.x = 0 // expected-error {{immutable value 'self.x' may only be initialized once}}
29+
}
30+
}
31+
32+
@Wrapper
33+
class ImmutableTest2 {
34+
let a: Int
35+
let b: String
36+
37+
init(a: Int, b: String) {
38+
self.a = 0
39+
self.a = a // expected-error {{immutable value 'self.a' may only be initialized once}}
40+
41+
self.b = b
42+
self.b = "" // expected-error {{immutable value 'self.b' may only be initialized once}}
43+
}
44+
}
45+
46+
// FIXME: Diagnostics should mention `self.b` or `self.a` and not `self.$_storage`
47+
48+
@Wrapper
49+
struct ImmutableTest3 {
50+
let a: Int
51+
let b: String
52+
53+
init() {
54+
self.b = "" // Ok
55+
self.a = 42 // Ok
56+
}
57+
58+
init(a: Int = 42) {
59+
self.a = a
60+
print(self.a) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$_storage' not initialized}}
61+
self.b = ""
62+
}
63+
64+
/* FIXME: This test is crashing in findFullInitializationPoints
65+
init(b: String) {
66+
self.b = b
67+
print(self.a)
68+
}
69+
*/
70+
71+
init(otherB: String?) {
72+
self.a = 0
73+
74+
if let b = otherB {
75+
self.b = b
76+
}
77+
} // expected-error {{return from initializer without initializing all stored properties}} expected-note {{'self.$_storage' not initialized}}
78+
79+
init(optB: String? = nil) {
80+
if let optB {
81+
self.b = optB
82+
print(self.b) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$_storage' not initialized}}
83+
} else {
84+
self.b = ""
85+
print(self.b) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$_storage' not initialized}}
86+
}
87+
88+
self.a = 0
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)