Skip to content

Commit cb0825b

Browse files
committed
[BuilderTransform] Lift all restrictions from variable decls when AST transform is enabled
Result builders would be able to declare: - uninitialized variables - variables with property wrappers (with and without explicit initializer) - default initializable variables i.e. `var x: Int?` - computed variables - `lazy` variables
1 parent e22fc3e commit cb0825b

File tree

2 files changed

+166
-33
lines changed

2 files changed

+166
-33
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,22 +1020,14 @@ class ResultBuilderTransform
10201020

10211021
if (auto *decl = element.dyn_cast<Decl *>()) {
10221022
switch (decl->getKind()) {
1023-
case DeclKind::PatternBinding: {
1024-
if (!isValidPatternBinding(cast<PatternBindingDecl>(decl)))
1025-
return failTransform(decl);
1026-
1027-
LLVM_FALLTHROUGH;
1028-
}
1029-
10301023
// Just ignore #if; the chosen children should appear in
10311024
// the surrounding context. This isn't good for source
10321025
// tools but it at least works.
10331026
case DeclKind::IfConfig:
10341027
// Skip #warning/#error; we'll handle them when applying
10351028
// the builder.
10361029
case DeclKind::PoundDiagnostic:
1037-
// Ignore variable declarations, because they're always
1038-
// handled within their enclosing pattern bindings.
1030+
case DeclKind::PatternBinding:
10391031
case DeclKind::Var:
10401032
case DeclKind::Param:
10411033
newBody.push_back(element);
@@ -1577,30 +1569,6 @@ class ResultBuilderTransform
15771569
return DoStmt::createImplicit(ctx, LabeledStmtInfo(), doBody);
15781570
}
15791571

1580-
bool isValidPatternBinding(PatternBindingDecl *PB) {
1581-
// Enforce some restrictions on local variables inside a result builder.
1582-
for (unsigned i : range(PB->getNumPatternEntries())) {
1583-
// The pattern binding must have an initial value expression.
1584-
if (!PB->isExplicitlyInitialized(i))
1585-
return false;
1586-
1587-
// Each variable bound by the pattern must be stored, and cannot
1588-
// have observers.
1589-
SmallVector<VarDecl *, 8> variables;
1590-
PB->getPattern(i)->collectVariables(variables);
1591-
1592-
for (auto *var : variables) {
1593-
if (!var->getImplInfo().isSimpleStored())
1594-
return false;
1595-
1596-
// Also check for invalid attributes.
1597-
TypeChecker::checkDeclAttributes(var);
1598-
}
1599-
}
1600-
1601-
return true;
1602-
}
1603-
16041572
UNSUPPORTED_STMT(Throw)
16051573
UNSUPPORTED_STMT(Return)
16061574
UNSUPPORTED_STMT(Yield)
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -enable-experimental-feature ResultBuilderASTTransform -module-name main -I %t -L %t %s -o %t/main %target-rpath(%t)
3+
// RUN: %target-codesign %t/main
4+
// RUN: %target-run %t/main | %FileCheck %s
5+
6+
// REQUIRES: executable_test
7+
8+
@propertyWrapper
9+
struct Wrapper<Value> {
10+
public var value: Value
11+
12+
init(wrappedValue: Value) {
13+
self.value = wrappedValue
14+
}
15+
16+
var projectedValue: Self { return self }
17+
18+
var wrappedValue: Value {
19+
get { self.value }
20+
set { self.value = newValue }
21+
}
22+
23+
func test() -> Int { 42 }
24+
}
25+
26+
extension Wrapper where Value: ExpressibleByNilLiteral {
27+
init() {
28+
self.value = nil
29+
}
30+
}
31+
32+
enum Either<T,U> {
33+
case first(T)
34+
case second(U)
35+
}
36+
37+
@resultBuilder
38+
struct TupleBuilder {
39+
static func buildBlock() -> () { }
40+
41+
static func buildBlock<T1>(_ t1: T1) -> T1 {
42+
return t1
43+
}
44+
45+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
46+
return (t1, t2)
47+
}
48+
49+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
50+
-> (T1, T2, T3) {
51+
return (t1, t2, t3)
52+
}
53+
54+
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
55+
-> (T1, T2, T3, T4) {
56+
return (t1, t2, t3, t4)
57+
}
58+
59+
static func buildBlock<T1, T2, T3, T4, T5>(
60+
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
61+
) -> (T1, T2, T3, T4, T5) {
62+
return (t1, t2, t3, t4, t5)
63+
}
64+
65+
static func buildDo<T>(_ value: T) -> T { return value }
66+
static func buildOptional<T>(_ value: T?) -> T? { return value }
67+
68+
static func buildEither<T,U>(first value: T) -> Either<T,U> {
69+
return .first(value)
70+
}
71+
static func buildEither<T,U>(second value: U) -> Either<T,U> {
72+
return .second(value)
73+
}
74+
}
75+
76+
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
77+
print(body(cond))
78+
}
79+
80+
tuplify(true) { cond in
81+
@Wrapper var x: Int?
82+
x
83+
x = 42
84+
x
85+
}
86+
// CHECK: (nil, (), Optional(42))
87+
88+
tuplify(true) { cond in
89+
@Wrapper var x: Int = 42
90+
x
91+
if cond {
92+
$x
93+
}
94+
}
95+
// CHECK: (42, Optional(main.Wrapper<Swift.Int>(value: 42)))
96+
97+
tuplify(true) { cond in
98+
@Wrapper(wrappedValue: 42) var x: Int
99+
100+
if cond && x == 42 {
101+
x = 30
102+
$x
103+
}
104+
105+
x
106+
}
107+
// CHECK: (Optional(((), main.Wrapper<Swift.Int>(value: 30))), 30)
108+
109+
tuplify(true) { cond in
110+
if cond {
111+
@Wrapper(wrappedValue: 42) var x: Int
112+
if $x.test() > 0 {
113+
x
114+
}
115+
}
116+
117+
""
118+
}
119+
// CHECK: (Optional(Optional(42)), "")
120+
121+
tuplify(true) { cond in
122+
var x: Int?
123+
124+
if cond {
125+
var y: Int?
126+
x = 1
127+
y = x
128+
y
129+
} else {
130+
x = 0
131+
}
132+
133+
x
134+
}
135+
// CHECK: (main.Either<((), (), Swift.Optional<Swift.Int>), ()>.first((), (), Optional(1)), Optional(1))
136+
137+
tuplify(true) { cond in
138+
""
139+
140+
var x: Int {
141+
get { 42 }
142+
}
143+
144+
if cond {
145+
x
146+
}
147+
148+
""
149+
}
150+
// CHECK: ("", Optional(42), "")
151+
152+
tuplify(true) { cond in
153+
lazy var x: Int = {
154+
42
155+
}()
156+
157+
if cond {
158+
x
159+
x = 0
160+
x
161+
}
162+
163+
""
164+
}
165+
// CHECK: (Optional((42, (), 0)), "")

0 commit comments

Comments
 (0)