Skip to content

Commit b1c27cb

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 15e17b2 commit b1c27cb

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
@@ -1015,22 +1015,14 @@ class ResultBuilderTransform
10151015

10161016
if (auto *decl = element.dyn_cast<Decl *>()) {
10171017
switch (decl->getKind()) {
1018-
case DeclKind::PatternBinding: {
1019-
if (!isValidPatternBinding(cast<PatternBindingDecl>(decl)))
1020-
return failTransform(decl);
1021-
1022-
LLVM_FALLTHROUGH;
1023-
}
1024-
10251018
// Just ignore #if; the chosen children should appear in
10261019
// the surrounding context. This isn't good for source
10271020
// tools but it at least works.
10281021
case DeclKind::IfConfig:
10291022
// Skip #warning/#error; we'll handle them when applying
10301023
// the builder.
10311024
case DeclKind::PoundDiagnostic:
1032-
// Ignore variable declarations, because they're always
1033-
// handled within their enclosing pattern bindings.
1025+
case DeclKind::PatternBinding:
10341026
case DeclKind::Var:
10351027
case DeclKind::Param:
10361028
newBody.push_back(element);
@@ -1567,30 +1559,6 @@ class ResultBuilderTransform
15671559
return DoStmt::createImplicit(ctx, LabeledStmtInfo(), doBody);
15681560
}
15691561

1570-
bool isValidPatternBinding(PatternBindingDecl *PB) {
1571-
// Enforce some restrictions on local variables inside a result builder.
1572-
for (unsigned i : range(PB->getNumPatternEntries())) {
1573-
// The pattern binding must have an initial value expression.
1574-
if (!PB->isExplicitlyInitialized(i))
1575-
return false;
1576-
1577-
// Each variable bound by the pattern must be stored, and cannot
1578-
// have observers.
1579-
SmallVector<VarDecl *, 8> variables;
1580-
PB->getPattern(i)->collectVariables(variables);
1581-
1582-
for (auto *var : variables) {
1583-
if (!var->getImplInfo().isSimpleStored())
1584-
return false;
1585-
1586-
// Also check for invalid attributes.
1587-
TypeChecker::checkDeclAttributes(var);
1588-
}
1589-
}
1590-
1591-
return true;
1592-
}
1593-
15941562
UNSUPPORTED_STMT(Throw)
15951563
UNSUPPORTED_STMT(Return)
15961564
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)