Skip to content

Commit 885f9ba

Browse files
committed
[Sema] Allow falling back to outer results from closure
If we encounter a variable declared after its use within a closure, we can fallback to using an outer result if present. This matches the behavior outside of a closure and generally seems more consistent with the behavior we have if we also find an inner result.
1 parent 7caf3eb commit 885f9ba

File tree

4 files changed

+26
-25
lines changed

4 files changed

+26
-25
lines changed

lib/Sema/PreCheckTarget.cpp

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,7 @@ static bool isMemberChainTail(Expr *expr, Expr *parent, MemberChainKind kind) {
393393
}
394394

395395
static bool isValidForwardReference(ValueDecl *D, DeclContext *DC,
396-
ValueDecl *&localDeclAfterUse,
397-
bool &shouldUseOuterResults) {
396+
ValueDecl *&localDeclAfterUse) {
398397
// Only VarDecls require declaration before use.
399398
auto *VD = dyn_cast<VarDecl>(D);
400399
if (!VD)
@@ -412,11 +411,6 @@ static bool isValidForwardReference(ValueDecl *D, DeclContext *DC,
412411
}
413412
if (isa<AbstractClosureExpr>(DC) ||
414413
(isa<FuncDecl>(DC) && cast<FuncDecl>(DC)->isDeferBody())) {
415-
// If we cross a closure, don't context, don't allow falling back to
416-
// an outer result if we have a forward reference. This preserves the
417-
// behavior prior to diagnosing this in Sema.
418-
if (isa<AbstractClosureExpr>(DC))
419-
shouldUseOuterResults = false;
420414
DC = DC->getParent();
421415
continue;
422416
}
@@ -594,20 +588,18 @@ static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC,
594588
Lookup = TypeChecker::lookupUnqualified(DC, LookupName, Loc, lookupOptions);
595589

596590
ValueDecl *localDeclAfterUse = nullptr;
597-
bool shouldUseOuterResults = true;
598591
AllDeclRefs = findNonMembers(
599592
Lookup.innerResults(), UDRE->getRefKind(),
600593
/*breakOnMember=*/true, ResultValues, [&](ValueDecl *D) {
601-
return isValidForwardReference(D, DC, localDeclAfterUse,
602-
shouldUseOuterResults);
594+
return isValidForwardReference(D, DC, localDeclAfterUse);
603595
});
604596

605597
// If local declaration after use is found, check outer results for
606598
// better matching candidates.
607599
if (ResultValues.empty() && localDeclAfterUse) {
608600
auto innerDecl = localDeclAfterUse;
609601
while (localDeclAfterUse) {
610-
if (!shouldUseOuterResults || Lookup.outerResults().empty()) {
602+
if (Lookup.outerResults().empty()) {
611603
Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name);
612604
Context.Diags.diagnose(innerDecl, diag::decl_declared_here,
613605
localDeclAfterUse);
@@ -620,8 +612,7 @@ static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC,
620612
AllDeclRefs = findNonMembers(
621613
Lookup.innerResults(), UDRE->getRefKind(),
622614
/*breakOnMember=*/true, ResultValues, [&](ValueDecl *D) {
623-
return isValidForwardReference(D, DC, localDeclAfterUse,
624-
shouldUseOuterResults);
615+
return isValidForwardReference(D, DC, localDeclAfterUse);
625616
});
626617
}
627618
}

test/NameLookup/use_before_declaration_shadowing.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,23 @@ func testTopLevel() {
5454
}
5555
do {
5656
_ = {
57-
let _: String = topLevelString // expected-error {{use of local variable 'topLevelString' before its declaration}}
57+
let _: String = topLevelString
5858
}
59-
let topLevelString = 0 // expected-note {{'topLevelString' declared here}}
59+
let topLevelString = 0
60+
_ = topLevelString
6061
}
6162
_ = {
6263
_ = {
63-
let _: String = topLevelString // expected-error {{use of local variable 'topLevelString' before its declaration}}
64+
let _: String = topLevelString
6465
}
65-
let topLevelString = 0 // expected-note {{'topLevelString' declared here}}
66+
let topLevelString = 0
67+
_ = topLevelString
6668
}
6769
func bar() {
6870
_ = {
69-
let _: String = topLevelString // expected-error {{use of local variable 'topLevelString' before its declaration}}
71+
let _: String = topLevelString
7072
}
71-
let topLevelString = 0 // expected-note {{'topLevelString' declared here}}
73+
let topLevelString = 0
74+
_ = topLevelString
7275
}
7376
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-swift-frontend %s -sil-verify-all -c
22

3-
// Report the error but don't crash.
3+
// Make sure we don't crash.
44

55
class TestUndefined {
66
private var stringList: [String]!
77

88
func dontCrash(strings: [String]) {
99
assert(stringList.allSatisfy({ $0 == stringList.first!}))
10-
// expected-error@-1 {{use of local variable 'stringList' before its declaration}}
1110
let stringList = strings.filter({ $0 == "a" })
12-
// expected-note@-1 {{'stringList' declared here}}
1311
}
1412
}

test/expr/expressions.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,21 @@ func test_lambda1() {
252252

253253
func test_lambda2() {
254254
// A recursive lambda.
255-
var fib = { (n: Int) -> Int in // expected-note 2{{'fib' declared here}}
255+
var fibLocal = { (n: Int) -> Int in // expected-note 2{{'fibLocal' declared here}}
256256
if (n < 2) {
257257
return n
258258
}
259259

260-
return fib(n-1)+fib(n-2) // expected-error 2{{use of local variable 'fib' before its declaration}}
260+
return fibLocal(n-1)+fibLocal(n-2) // expected-error 2{{use of local variable 'fibLocal' before its declaration}}
261+
}
262+
263+
var fib = { (n: Int) -> Int in
264+
if (n < 2) {
265+
return n
266+
}
267+
268+
// These resolve to the top-level function.
269+
return fib(n-1)+fib(n-2)
261270
}
262271
}
263272

0 commit comments

Comments
 (0)