Skip to content

Commit c0ca9ad

Browse files
committed
Sema: Reject non-stored local variables in result builders
We would previously reject properties without an initializer expression, but this is not quite sufficient, because of 'lazy', property wrappers, and observers. We could probably support all three in result builder contexts, but for now, let's diagnose them like other computed properties, instead of crashing. Fixes part of <rdar://problem/73545981>.
1 parent 09f0838 commit c0ca9ad

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,27 @@ class BuilderClosureVisitor
268268
}
269269

270270
void visitPatternBindingDecl(PatternBindingDecl *patternBinding) {
271-
// If any of the entries lacks an initializer, don't handle this node.
272-
if (!llvm::all_of(range(patternBinding->getNumPatternEntries()),
273-
[&](unsigned index) {
274-
return patternBinding->isExplicitlyInitialized(index);
275-
})) {
276-
if (!unhandledNode)
277-
unhandledNode = patternBinding;
278-
return;
271+
// Enforce some restrictions on local variables inside a result builder.
272+
for (unsigned i : range(patternBinding->getNumPatternEntries())) {
273+
// The pattern binding must have an initial value expression.
274+
if (!patternBinding->isExplicitlyInitialized(i)) {
275+
if (!unhandledNode)
276+
unhandledNode = patternBinding;
277+
return;
278+
}
279+
280+
// Each variable bound by the pattern must be stored, and cannot
281+
// have observers.
282+
SmallVector<VarDecl *, 8> variables;
283+
patternBinding->getPattern(i)->collectVariables(variables);
284+
285+
for (auto *var : variables) {
286+
if (!var->getImplInfo().isSimpleStored()) {
287+
if (!unhandledNode)
288+
unhandledNode = patternBinding;
289+
return;
290+
}
291+
}
279292
}
280293

281294
// If there is a constraint system, generate constraints for the pattern
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
@resultBuilder
4+
struct DummyBuilder { // expected-note 5 {{struct 'DummyBuilder' declared here}}
5+
static func buildBlock<T>(_ t: T) -> T {
6+
return t
7+
}
8+
}
9+
10+
func dummy<T>(@DummyBuilder _: () -> T) {}
11+
12+
dummy {
13+
var computedVar: Int { return 123 } // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
14+
()
15+
}
16+
17+
dummy {
18+
lazy var lazyVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
19+
()
20+
}
21+
22+
dummy {
23+
var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
24+
didSet {}
25+
}
26+
27+
()
28+
}
29+
30+
dummy {
31+
var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
32+
willSet {}
33+
}
34+
35+
()
36+
}
37+
38+
@propertyWrapper struct Wrapper {
39+
var wrappedValue: Int
40+
}
41+
42+
dummy {
43+
@Wrapper var wrappedVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
44+
()
45+
}

0 commit comments

Comments
 (0)