Skip to content

Commit 06fc9c5

Browse files
committed
Sema: Simulate old name lookup behavior when parser lookup is off
Before performing an UnqualifiedLookup with Flags::IncludeOuterResults turned on, call ASTScope::lookupSingleLocalDecl() to find local bindings that precede the current source location. If this fails, we perform an unqualified lookup to try to find forward references to captures, type members, and top-level declarations.
1 parent 2838838 commit 06fc9c5

File tree

2 files changed

+89
-47
lines changed

2 files changed

+89
-47
lines changed

lib/Sema/PreCheckExpr.cpp

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,77 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
351351
// name/module qualifier to access top-level name.
352352
lookupOptions |= NameLookupFlags::IncludeOuterResults;
353353

354-
if (Loc.isInvalid())
355-
DC = DC->getModuleScopeContext();
354+
LookupResult Lookup;
356355

357-
auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions);
356+
bool AllDeclRefs = true;
357+
SmallVector<ValueDecl*, 4> ResultValues;
358358

359359
auto &Context = DC->getASTContext();
360+
if (Context.LangOpts.DisableParserLookup) {
361+
// First, look for a local binding in scope.
362+
if (Loc.isValid() && !Name.isOperator()) {
363+
SmallVector<ValueDecl *, 2> localDecls;
364+
ASTScope::lookupLocalDecls(DC->getParentSourceFile(),
365+
Name.getFullName(), Loc,
366+
/*stopAfterInnermostBraceStmt=*/false,
367+
ResultValues);
368+
for (auto *localDecl : ResultValues) {
369+
Lookup.add(LookupResultEntry(localDecl), /*isOuter=*/false);
370+
}
371+
}
372+
}
373+
374+
if (!Lookup) {
375+
// Now, look for all local bindings, even forward references, as well
376+
// as type members and top-level declarations.
377+
if (Loc.isInvalid())
378+
DC = DC->getModuleScopeContext();
379+
380+
Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions);
381+
382+
ValueDecl *localDeclAfterUse = nullptr;
383+
auto isValid = [&](ValueDecl *D) {
384+
// If we find something in the current context, it must be a forward
385+
// reference, because otherwise if it was in scope, it would have
386+
// been returned by the call to ASTScope::lookupLocalDecls() above.
387+
if (D->getDeclContext()->isLocalContext() &&
388+
D->getDeclContext() == DC &&
389+
(Context.LangOpts.DisableParserLookup ||
390+
(Loc.isValid() && D->getLoc().isValid() &&
391+
Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) &&
392+
!isa<TypeDecl>(D)))) {
393+
localDeclAfterUse = D;
394+
return false;
395+
}
396+
return true;
397+
};
398+
AllDeclRefs =
399+
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
400+
/*breakOnMember=*/true, ResultValues, isValid);
401+
402+
// If local declaration after use is found, check outer results for
403+
// better matching candidates.
404+
if (ResultValues.empty() && localDeclAfterUse) {
405+
auto innerDecl = localDeclAfterUse;
406+
while (localDeclAfterUse) {
407+
if (Lookup.outerResults().empty()) {
408+
Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name);
409+
Context.Diags.diagnose(innerDecl, diag::decl_declared_here,
410+
localDeclAfterUse->getName());
411+
Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange());
412+
return error;
413+
}
414+
415+
Lookup.shiftDownResults();
416+
ResultValues.clear();
417+
localDeclAfterUse = nullptr;
418+
AllDeclRefs =
419+
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
420+
/*breakOnMember=*/true, ResultValues, isValid);
421+
}
422+
}
423+
}
424+
360425
if (!Lookup) {
361426
// If we failed lookup of an operator, check to see if this is a range
362427
// operator misspelling. Otherwise try to diagnose a juxtaposition
@@ -487,50 +552,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
487552

488553
// FIXME: Need to refactor the way we build an AST node from a lookup result!
489554

490-
SmallVector<ValueDecl*, 4> ResultValues;
491-
ValueDecl *localDeclAfterUse = nullptr;
492-
auto isValid = [&](ValueDecl *D) {
493-
// FIXME: The source-location checks won't make sense once
494-
// EnableASTScopeLookup is the default.
495-
//
496-
// Note that we allow forward references to types, because they cannot
497-
// capture.
498-
if (Loc.isValid() && D->getLoc().isValid() &&
499-
D->getDeclContext()->isLocalContext() &&
500-
D->getDeclContext() == DC &&
501-
Context.SourceMgr.isBeforeInBuffer(Loc, D->getLoc()) &&
502-
!isa<TypeDecl>(D)) {
503-
localDeclAfterUse = D;
504-
return false;
505-
}
506-
return true;
507-
};
508-
bool AllDeclRefs =
509-
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
510-
/*breakOnMember=*/true, ResultValues, isValid);
511-
512-
// If local declaration after use is found, check outer results for
513-
// better matching candidates.
514-
if (localDeclAfterUse) {
515-
auto innerDecl = localDeclAfterUse;
516-
while (localDeclAfterUse) {
517-
if (Lookup.outerResults().empty()) {
518-
Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name);
519-
Context.Diags.diagnose(innerDecl, diag::decl_declared_here,
520-
localDeclAfterUse->getName());
521-
Expr *error = new (Context) ErrorExpr(UDRE->getSourceRange());
522-
return error;
523-
}
524-
525-
Lookup.shiftDownResults();
526-
ResultValues.clear();
527-
localDeclAfterUse = nullptr;
528-
AllDeclRefs =
529-
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
530-
/*breakOnMember=*/true, ResultValues, isValid);
531-
}
532-
}
533-
534555
// If we have an unambiguous reference to a type decl, form a TypeExpr.
535556
if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary &&
536557
isa<TypeDecl>(Lookup[0].getValueDecl())) {

test/NameLookup/edge-cases.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-typecheck-verify-swift -disable-parser-lookup
2+
3+
struct A {}
4+
struct B {}
5+
6+
func other() -> A {}
7+
8+
func takesB(_: B) {}
9+
10+
func multipleLocalResults1() {
11+
func other() -> B {}
12+
let result = other()
13+
takesB(result)
14+
}
15+
16+
func multipleLocalResults2() {
17+
func other() -> B {}
18+
let result = other()
19+
takesB(result)
20+
let other: Int = 123 // expected-warning {{never used}}
21+
}

0 commit comments

Comments
 (0)