Skip to content

Commit ce1ffd7

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 ce1ffd7

File tree

2 files changed

+167
-33
lines changed

2 files changed

+167
-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: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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+
// REQUIRES: asserts
8+
9+
@propertyWrapper
10+
struct Wrapper<Value> {
11+
public var value: Value
12+
13+
init(wrappedValue: Value) {
14+
self.value = wrappedValue
15+
}
16+
17+
var projectedValue: Self { return self }
18+
19+
var wrappedValue: Value {
20+
get { self.value }
21+
set { self.value = newValue }
22+
}
23+
24+
func test() -> Int { 42 }
25+
}
26+
27+
extension Wrapper where Value: ExpressibleByNilLiteral {
28+
init() {
29+
self.value = nil
30+
}
31+
}
32+
33+
enum Either<T,U> {
34+
case first(T)
35+
case second(U)
36+
}
37+
38+
@resultBuilder
39+
struct TupleBuilder {
40+
static func buildBlock() -> () { }
41+
42+
static func buildBlock<T1>(_ t1: T1) -> T1 {
43+
return t1
44+
}
45+
46+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
47+
return (t1, t2)
48+
}
49+
50+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
51+
-> (T1, T2, T3) {
52+
return (t1, t2, t3)
53+
}
54+
55+
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
56+
-> (T1, T2, T3, T4) {
57+
return (t1, t2, t3, t4)
58+
}
59+
60+
static func buildBlock<T1, T2, T3, T4, T5>(
61+
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
62+
) -> (T1, T2, T3, T4, T5) {
63+
return (t1, t2, t3, t4, t5)
64+
}
65+
66+
static func buildDo<T>(_ value: T) -> T { return value }
67+
static func buildOptional<T>(_ value: T?) -> T? { return value }
68+
69+
static func buildEither<T,U>(first value: T) -> Either<T,U> {
70+
return .first(value)
71+
}
72+
static func buildEither<T,U>(second value: U) -> Either<T,U> {
73+
return .second(value)
74+
}
75+
}
76+
77+
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
78+
print(body(cond))
79+
}
80+
81+
tuplify(true) { cond in
82+
@Wrapper var x: Int?
83+
x
84+
x = 42
85+
x
86+
}
87+
// CHECK: (nil, (), Optional(42))
88+
89+
tuplify(true) { cond in
90+
@Wrapper var x: Int = 42
91+
x
92+
if cond {
93+
$x
94+
}
95+
}
96+
// CHECK: (42, Optional(main.Wrapper<Swift.Int>(value: 42)))
97+
98+
tuplify(true) { cond in
99+
@Wrapper(wrappedValue: 42) var x: Int
100+
101+
if cond && x == 42 {
102+
x = 30
103+
$x
104+
}
105+
106+
x
107+
}
108+
// CHECK: (Optional(((), main.Wrapper<Swift.Int>(value: 30))), 30)
109+
110+
tuplify(true) { cond in
111+
if cond {
112+
@Wrapper(wrappedValue: 42) var x: Int
113+
if $x.test() > 0 {
114+
x
115+
}
116+
}
117+
118+
""
119+
}
120+
// CHECK: (Optional(Optional(42)), "")
121+
122+
tuplify(true) { cond in
123+
var x: Int?
124+
125+
if cond {
126+
var y: Int?
127+
x = 1
128+
y = x
129+
y
130+
} else {
131+
x = 0
132+
}
133+
134+
x
135+
}
136+
// CHECK: (main.Either<((), (), Swift.Optional<Swift.Int>), ()>.first((), (), Optional(1)), Optional(1))
137+
138+
tuplify(true) { cond in
139+
""
140+
141+
var x: Int {
142+
get { 42 }
143+
}
144+
145+
if cond {
146+
x
147+
}
148+
149+
""
150+
}
151+
// CHECK: ("", Optional(42), "")
152+
153+
tuplify(true) { cond in
154+
lazy var x: Int = {
155+
42
156+
}()
157+
158+
if cond {
159+
x
160+
x = 0
161+
x
162+
}
163+
164+
""
165+
}
166+
// CHECK: (Optional((42, (), 0)), "")

0 commit comments

Comments
 (0)