Skip to content

Commit cf71dc9

Browse files
authored
Merge pull request #38878 from xedin/improve-var--without-init-error-in-resut-builder
[ResultBuilders] Improve diagnostics of unsupported variable declarations
2 parents c2ed947 + 994f9a9 commit cf71dc9

File tree

5 files changed

+104
-7
lines changed

5 files changed

+104
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5882,6 +5882,12 @@ NOTE(result_builder_missing_build_array, none,
58825882
NOTE(result_builder_missing_build_limited_availability, none,
58835883
"add 'buildLimitedAvailability(_:)' to the result "
58845884
"builder %0 to erase type information for less-available types", (Type))
5885+
ERROR(result_builder_requires_explicit_var_initialization,none,
5886+
"local variable%select{| '%1'}0 requires explicit initializer to be used with "
5887+
"result builder %2", (bool, StringRef, DeclName))
5888+
ERROR(cannot_declare_computed_var_in_result_builder,none,
5889+
"cannot declare local %select{lazy|wrapped|computed|observed}0 variable "
5890+
"in result builder", (unsigned))
58855891

58865892
//------------------------------------------------------------------------------
58875893
// MARK: Tuple Shuffle Diagnostics

lib/Sema/CSDiagnostics.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5870,8 +5870,84 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) {
58705870
return false;
58715871
}
58725872

5873+
bool SkipUnhandledConstructInResultBuilderFailure::diagnosePatternBinding(
5874+
PatternBindingDecl *PB) const {
5875+
bool diagnosed = false;
5876+
5877+
for (unsigned i : range(PB->getNumPatternEntries())) {
5878+
auto *pattern = PB->getPattern(i);
5879+
5880+
// Each variable bound by the pattern must be stored and cannot have
5881+
// observers.
5882+
{
5883+
SmallVector<VarDecl *, 8> variables;
5884+
pattern->collectVariables(variables);
5885+
5886+
bool diagnosedStorage = false;
5887+
for (auto *var : variables)
5888+
diagnosedStorage |= diagnoseStorage(var);
5889+
5890+
// if storage has been diagnosed, let's move to the next entry.
5891+
if (diagnosedStorage) {
5892+
diagnosed = true;
5893+
continue;
5894+
}
5895+
}
5896+
5897+
// Diagnose all of the patterns without explicit initializers.
5898+
if (PB->isExplicitlyInitialized(i))
5899+
continue;
5900+
5901+
StringRef name;
5902+
5903+
if (auto *TP = dyn_cast<TypedPattern>(pattern)) {
5904+
if (auto *NP = dyn_cast<NamedPattern>(TP->getSubPattern()))
5905+
name = NP->getNameStr();
5906+
}
5907+
5908+
emitDiagnosticAt(pattern->getLoc(),
5909+
diag::result_builder_requires_explicit_var_initialization,
5910+
!name.empty(), name, builder->getName())
5911+
.fixItInsertAfter(pattern->getEndLoc(), " = <#value#>");
5912+
5913+
diagnosed = true;
5914+
}
5915+
5916+
return diagnosed;
5917+
}
5918+
5919+
bool SkipUnhandledConstructInResultBuilderFailure::diagnoseStorage(
5920+
VarDecl *var) const {
5921+
enum class PropertyKind : unsigned { lazy, wrapped, computed, observed };
5922+
5923+
if (var->getImplInfo().isSimpleStored())
5924+
return false;
5925+
5926+
PropertyKind kind;
5927+
if (var->getAttrs().hasAttribute<LazyAttr>()) {
5928+
kind = PropertyKind::lazy;
5929+
} else if (var->hasAttachedPropertyWrapper()) {
5930+
kind = PropertyKind::wrapped;
5931+
} else if (var->hasObservers()) {
5932+
kind = PropertyKind::observed;
5933+
} else {
5934+
kind = PropertyKind::computed;
5935+
}
5936+
5937+
emitDiagnosticAt(var, diag::cannot_declare_computed_var_in_result_builder,
5938+
static_cast<unsigned>(kind));
5939+
return true;
5940+
}
5941+
58735942
void SkipUnhandledConstructInResultBuilderFailure::diagnosePrimary(
58745943
bool asNote) {
5944+
5945+
if (auto *decl = unhandled.dyn_cast<Decl *>()) {
5946+
auto *PB = dyn_cast<PatternBindingDecl>(decl);
5947+
if (PB && diagnosePatternBinding(PB))
5948+
return;
5949+
}
5950+
58755951
if (auto stmt = unhandled.dyn_cast<Stmt *>()) {
58765952
emitDiagnostic(asNote ? diag::note_result_builder_control_flow
58775953
: diag::result_builder_control_flow,

lib/Sema/CSDiagnostics.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,13 @@ class SkipUnhandledConstructInResultBuilderFailure final
18621862

18631863
bool diagnoseAsError() override;
18641864
bool diagnoseAsNote() override;
1865+
1866+
private:
1867+
/// Tailored diagnostics for an unsupported variable declaration.
1868+
bool diagnosePatternBinding(PatternBindingDecl *PB) const;
1869+
1870+
/// Tailored diagnostics for lazy/wrapped/computed variable declarations.
1871+
bool diagnoseStorage(VarDecl *var) const;
18651872
};
18661873

18671874
/// Diagnose situation when a single "tuple" parameter is given N arguments e.g.

test/Constraints/result_builder_diags.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enum Either<T,U> {
66
}
77

88
@resultBuilder
9-
struct TupleBuilder { // expected-note 2 {{struct 'TupleBuilder' declared here}}
9+
struct TupleBuilder { // expected-note 3 {{struct 'TupleBuilder' declared here}}
1010
static func buildBlock() -> () { }
1111

1212
static func buildBlock<T1>(_ t1: T1) -> T1 {
@@ -99,10 +99,18 @@ func testDiags() {
9999
tuplify(true) { _ in
100100
17
101101
let x = 17
102-
let y: Int // expected-error{{closure containing a declaration cannot be used with result builder 'TupleBuilder'}}
102+
let y: Int // expected-error{{local variable 'y' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{15-15= = <#value#>}}
103103
x + 25
104104
}
105105

106+
tuplify(true) { _ in
107+
17
108+
let y: Int, z: String
109+
// expected-error@-1 {{local variable 'y' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{15-15= = <#value#>}}
110+
// expected-error@-2 {{local variable 'z' requires explicit initializer to be used with result builder 'TupleBuilder'}} {{26-26= = <#value#>}}
111+
y + 25
112+
}
113+
106114
// Statements unsupported by the particular builder.
107115
tuplifyWithoutIf(true) {
108116
if $0 { // expected-error{{closure containing control flow statement cannot be used with result builder 'TupleBuilderWithoutIf'}}

test/Constraints/result_builder_invalid_vars.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ struct DummyBuilder { // expected-note 5 {{struct 'DummyBuilder' declared here}}
1010
func dummy<T>(@DummyBuilder _: () -> T) {}
1111

1212
dummy {
13-
var computedVar: Int { return 123 } // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
13+
var computedVar: Int { return 123 } // expected-error {{cannot declare local computed variable in result builder}}
1414
()
1515
}
1616

1717
dummy {
18-
lazy var lazyVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
18+
lazy var lazyVar: Int = 123 // expected-error {{cannot declare local lazy variable in result builder}}
1919
()
2020
}
2121

2222
dummy {
23-
var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
23+
var observedVar: Int = 123 { // expected-error {{cannot declare local observed variable in result builder}}
2424
didSet {}
2525
}
2626

2727
()
2828
}
2929

3030
dummy {
31-
var observedVar: Int = 123 { // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
31+
var observedVar: Int = 123 { // expected-error {{cannot declare local observed variable in result builder}}
3232
willSet {}
3333
}
3434

@@ -40,7 +40,7 @@ dummy {
4040
}
4141

4242
dummy {
43-
@Wrapper var wrappedVar: Int = 123 // expected-error {{closure containing a declaration cannot be used with result builder 'DummyBuilder'}}
43+
@Wrapper var wrappedVar: Int = 123 // expected-error {{cannot declare local wrapped variable in result builder}}
4444
()
4545
}
4646

0 commit comments

Comments
 (0)