Skip to content

Commit d89f6cd

Browse files
committed
[TypeChecker] PropertyWrappers: Re-parent any captures from wrapped to backing property
In the situations where initializer of property wrapper attribute has any kind of captures i.e. `{ [x = ...] ... }` they should be re-parented to the backing property because that's actually where the expression gets used - initializer of the backing property, e.g: ```swift struct Test { @wrapper([1, 2, 3].map { [x = 42] ... }) var x: Int } ``` is transformed into: ```swift struct Test { var x: Int { get { ... } set { ... } } var _x: Wrapper<Int> = Wrapper(wrappedValue: [1, 2, 3].map { [x = 42] ... }) } ``` Resolves: #61570 Resolves: rdar://problem/101813792
1 parent 89c032c commit d89f6cd

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,13 @@ namespace {
9898
return Action::SkipChildren(E);
9999
}
100100

101-
// Capture lists need to be reparented to enclosing autoclosures.
102101
if (auto CapE = dyn_cast<CaptureListExpr>(E)) {
103-
if (isa<AutoClosureExpr>(ParentDC)) {
102+
// Capture lists need to be reparented to enclosing autoclosures
103+
// and/or initializers of property wrapper backing properties
104+
// (because they subsume initializers associated with wrapped
105+
// properties).
106+
if (isa<AutoClosureExpr>(ParentDC) ||
107+
isPropertyWrapperBackingPropertyInitContext(ParentDC)) {
104108
for (auto &Cap : CapE->getCaptureList()) {
105109
Cap.PBD->setDeclContext(ParentDC);
106110
Cap.getVar()->setDeclContext(ParentDC);
@@ -136,6 +140,21 @@ namespace {
136140
// variables.
137141
return Action::VisitChildrenIf(isa<PatternBindingDecl>(D));
138142
}
143+
144+
private:
145+
static bool isPropertyWrapperBackingPropertyInitContext(DeclContext *DC) {
146+
auto *init = dyn_cast<PatternBindingInitializer>(DC);
147+
if (!init)
148+
return false;
149+
150+
if (auto *PB = init->getBinding()) {
151+
auto *var = PB->getSingleVar();
152+
return var && var->getOriginalWrappedProperty(
153+
PropertyWrapperSynthesizedPropertyKind::Backing);
154+
}
155+
156+
return false;
157+
}
139158
};
140159

141160
static DeclName getDescriptiveName(AbstractFunctionDecl *AFD) {

test/Sema/property_wrappers.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,40 @@ struct S2_58201 {
8383
// CHECK: autoclosure_expr implicit type='() -> Bool' location={{.*}}.swift:[[@LINE+1]]:26 range=[{{.+}}] discriminator=1 escaping
8484
@W_58201 var b: Bool = false
8585
}
86+
87+
// https://github.com/apple/swift/issues/61570
88+
// rdar://problem/101813792 - Check that captured variables are re-parented to backing var initializer
89+
do {
90+
@propertyWrapper
91+
struct Wrapper {
92+
init(_: String) {}
93+
94+
var wrappedValue: Int { return 0 }
95+
}
96+
97+
struct TestExplicitCapture {
98+
@Wrapper("\([1,2,3].map({[x = 42] in "\($0 + x)" }).reduce("", +))")
99+
var wrapped: Int // Ok, becomes var _wrapped: Wrapper<Int> = Wrapper(wrappedValue: "\([1,2,3].map({[x = 42] in "\($0 + x)" }).reduce("", +)))
100+
}
101+
102+
@propertyWrapper
103+
struct Option {
104+
let help: String
105+
var value: Double = 0.0
106+
107+
var wrappedValue: Double {
108+
get { value }
109+
set { value = newValue }
110+
}
111+
}
112+
113+
enum TestEnum: String, CaseIterable {
114+
case hello = "hello"
115+
case world = "world"
116+
}
117+
118+
struct TestImplicitCapture {
119+
@Option(help: "Values: \(TestEnum.allCases.map(\.rawValue))")
120+
var value // Ok - $kp$ of key path is captured implicitly by backing variable init
121+
}
122+
}

0 commit comments

Comments
 (0)