Skip to content

Commit 14b344f

Browse files
authored
Merge pull request #17183 from slavapestov/lazy-init-throws-4.2
[Sema] Correctly reject throwing apply expressions in lazy property initializers [4.2]
2 parents 8f765d0 + e88f063 commit 14b344f

File tree

3 files changed

+137
-21
lines changed

3 files changed

+137
-21
lines changed

lib/Sema/TypeCheckError.cpp

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,14 @@ class Context {
873873
llvm_unreachable("invalid classify result");
874874
}
875875

876+
static Context getContextForPatternBinding(PatternBindingDecl *pbd) {
877+
if (!pbd->isStatic() && pbd->getDeclContext()->isTypeContext()) {
878+
return Context(Kind::IVarInitializer);
879+
} else {
880+
return Context(Kind::GlobalVarInitializer);
881+
}
882+
}
883+
876884
Kind TheKind;
877885
bool DiagnoseErrorOnTry = false;
878886
DeclContext *RethrowsDC = nullptr;
@@ -895,6 +903,23 @@ class Context {
895903
result.RethrowsDC = D;
896904
return result;
897905
}
906+
907+
// HACK: If the decl is the synthesized getter for a 'lazy' property, then
908+
// treat the context as a property initializer in order to produce a better
909+
// diagnostic; the only code we should be diagnosing on is within the
910+
// initializer expression that has been transplanted from the var's pattern
911+
// binding decl. We don't perform the analysis on the initializer while it's
912+
// still a part of that PBD, as it doesn't get a solution applied there.
913+
if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
914+
if (auto *var = dyn_cast<VarDecl>(accessor->getStorage())) {
915+
if (accessor->isGetter() && var->getAttrs().hasAttribute<LazyAttr>()) {
916+
auto *pbd = var->getParentPatternBinding();
917+
assert(pbd && "lazy var didn't have a pattern binding decl");
918+
return getContextForPatternBinding(pbd);
919+
}
920+
}
921+
}
922+
898923
return Context(getKindForFunctionBody(
899924
D->getInterfaceType(), D->getNumParameterLists()));
900925
}
@@ -904,14 +929,10 @@ class Context {
904929
return Context(Kind::DefaultArgument);
905930
}
906931

907-
auto binding = cast<PatternBindingInitializer>(init)->getBinding();
932+
auto *binding = cast<PatternBindingInitializer>(init)->getBinding();
908933
assert(!binding->getDeclContext()->isLocalContext() &&
909934
"setting up error context for local pattern binding?");
910-
if (!binding->isStatic() && binding->getDeclContext()->isTypeContext()) {
911-
return Context(Kind::IVarInitializer);
912-
} else {
913-
return Context(Kind::GlobalVarInitializer);
914-
}
935+
return getContextForPatternBinding(binding);
915936
}
916937

917938
static Context forEnumElementInitializer(EnumElementDecl *elt) {
@@ -1409,7 +1430,8 @@ class CheckErrorCoverage : public ErrorHandlingWalker<CheckErrorCoverage> {
14091430

14101431
// HACK: functions can get queued multiple times in
14111432
// definedFunctions, so be sure to be idempotent.
1412-
if (!E->isThrowsSet()) {
1433+
if (!E->isThrowsSet() &&
1434+
classification.getResult() != ThrowingKind::Invalid) {
14131435
E->setThrows(classification.getResult() == ThrowingKind::RethrowingOnly ||
14141436
classification.getResult() == ThrowingKind::Throws);
14151437
}
@@ -1483,6 +1505,9 @@ class CheckErrorCoverage : public ErrorHandlingWalker<CheckErrorCoverage> {
14831505
Flags.set(ContextFlags::HasAnyThrowSite);
14841506
if (requiresTry) Flags.set(ContextFlags::HasTryThrowSite);
14851507

1508+
// We set the throwing bit of an apply expr after performing this
1509+
// analysis, so ensure we don't emit duplicate diagnostics for functions
1510+
// that have been queued multiple times.
14861511
if (auto expr = E.dyn_cast<Expr*>())
14871512
if (auto apply = dyn_cast<ApplyExpr>(expr))
14881513
if (apply->isThrowsSet())

test/Parse/errors.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,6 @@ func testAutoclosures() throws {
8686
takesThrowingAutoclosure(genNoError())
8787
}
8888

89-
struct IllegalContext {
90-
var x: Int = genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
91-
92-
func foo(_ x: Int = genError()) {} // expected-error {{call can throw, but errors cannot be thrown out of a default argument}}
93-
94-
func catcher() throws {
95-
do {
96-
_ = try genError()
97-
} catch MSV.CarriesInt(genError()) { // expected-error {{call can throw, but errors cannot be thrown out of a catch pattern}}
98-
} catch MSV.CarriesInt(let i) where i == genError() { // expected-error {{call can throw, but errors cannot be thrown out of a catch guard expression}}
99-
}
100-
}
101-
}
102-
10389
func illformed() throws {
10490
do {
10591
_ = try genError()

test/decl/func/throwing_functions.swift

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,108 @@ let _ = rdar33040113() // expected-error {{call can throw but is not marked with
153153
// expected-note@-1 {{did you mean to use 'try'?}} {{9-9=try }}
154154
// expected-note@-2 {{did you mean to handle error as optional value?}} {{9-9=try? }}
155155
// expected-note@-3 {{did you mean to disable error propagation?}} {{9-9=try! }}
156+
157+
enum MSV : Error {
158+
case Foo, Bar, Baz
159+
case CarriesInt(Int)
160+
}
161+
162+
func genError() throws -> Int { throw MSV.Foo }
163+
164+
struct IllegalContext {
165+
var x1: Int = genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
166+
167+
let x2 = genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
168+
169+
var x3 = try genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
170+
171+
let x4: Int = try genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
172+
173+
var x5 = B() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
174+
175+
var x6 = try B() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
176+
177+
var x7 = { // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
178+
return try genError()
179+
}()
180+
181+
var x8: Int = {
182+
do {
183+
return genError() // expected-error {{call can throw but is not marked with 'try'}}
184+
// expected-note@-1 {{did you mean to use 'try'?}}
185+
// expected-note@-2 {{did you mean to handle error as optional value?}}
186+
// expected-note@-3 {{did you mean to disable error propagation?}}
187+
} catch {
188+
return 0
189+
}
190+
}()
191+
192+
var x9: Int = {
193+
do {
194+
return try genError()
195+
} catch {
196+
return 0
197+
}
198+
}()
199+
200+
var x10: B = {
201+
do {
202+
return try B()
203+
} catch {
204+
return B(foo: 0)
205+
}
206+
}()
207+
208+
lazy var y1: Int = genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
209+
210+
lazy var y2 = genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
211+
212+
lazy var y3 = try genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
213+
214+
lazy var y4: Int = try genError() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
215+
216+
lazy var y5 = B() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
217+
218+
lazy var y6 = try B() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
219+
220+
lazy var y7 = { // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
221+
return try genError()
222+
}()
223+
224+
lazy var y8: Int = {
225+
do {
226+
return genError() // expected-error {{call can throw but is not marked with 'try'}}
227+
// expected-note@-1 {{did you mean to use 'try'?}}
228+
// expected-note@-2 {{did you mean to handle error as optional value?}}
229+
// expected-note@-3 {{did you mean to disable error propagation?}}
230+
} catch {
231+
return 0
232+
}
233+
}()
234+
235+
lazy var y9: Int = {
236+
do {
237+
return try genError()
238+
} catch {
239+
return 0
240+
}
241+
}()
242+
243+
lazy var y10: B = {
244+
do {
245+
return try B()
246+
} catch {
247+
return B(foo: 0)
248+
}
249+
}()
250+
251+
func foo(_ x: Int = genError()) {} // expected-error {{call can throw, but errors cannot be thrown out of a default argument}}
252+
253+
func catcher() throws {
254+
do {
255+
_ = try genError()
256+
} catch MSV.CarriesInt(genError()) { // expected-error {{call can throw, but errors cannot be thrown out of a catch pattern}}
257+
} catch MSV.CarriesInt(let i) where i == genError() { // expected-error {{call can throw, but errors cannot be thrown out of a catch guard expression}}
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)