Skip to content

Commit e0de61c

Browse files
authored
Merge pull request #85135 from hamishknight/lazy-fix
[AST] Avoid exposing `lazy` local storage var to name lookup
2 parents 73b03ed + 55ebd07 commit e0de61c

File tree

13 files changed

+62
-35
lines changed

13 files changed

+62
-35
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6724,13 +6724,18 @@ class VarDecl : public AbstractStorageDecl {
67246724
Bits.VarDecl.IsDebuggerVar = IsDebuggerVar;
67256725
}
67266726

6727-
/// Visit all auxiliary declarations to this VarDecl.
6727+
/// Visit all auxiliary variables for this VarDecl.
67286728
///
6729-
/// An auxiliary declaration is a declaration synthesized by the compiler to support
6730-
/// this VarDecl, such as synthesized property wrapper variables.
6729+
/// An auxiliary variable is one that is synthesized by the compiler to
6730+
/// support this VarDecl, such as synthesized property wrapper variables.
67316731
///
6732-
/// \note this function only visits auxiliary decls that are not part of the AST.
6733-
void visitAuxiliaryDecls(llvm::function_ref<void(VarDecl *)>) const;
6732+
/// \param forNameLookup If \c true, will only visit auxiliary variables that
6733+
/// may appear in name lookup results.
6734+
///
6735+
/// \note this function only visits auxiliary variables that are not part of
6736+
/// the AST.
6737+
void visitAuxiliaryVars(bool forNameLookup,
6738+
llvm::function_ref<void(VarDecl *)>) const;
67346739

67356740
/// Is this the synthesized storage for a 'lazy' property?
67366741
bool isLazyStorageProperty() const {

lib/AST/Decl.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ void Decl::visitAuxiliaryDecls(
507507
}
508508
}
509509

510-
// FIXME: fold VarDecl::visitAuxiliaryDecls into this.
510+
// FIXME: fold VarDecl::visitAuxiliaryVars into this.
511511
}
512512

513513
void Decl::forEachAttachedMacro(MacroRole role,
@@ -8783,14 +8783,22 @@ bool VarDecl::hasStorageOrWrapsStorage() const {
87838783
return false;
87848784
}
87858785

8786-
void VarDecl::visitAuxiliaryDecls(llvm::function_ref<void(VarDecl *)> visit) const {
8786+
void VarDecl::visitAuxiliaryVars(
8787+
bool forNameLookup, llvm::function_ref<void(VarDecl *)> visit) const {
87878788
if (getDeclContext()->isTypeContext() ||
87888789
(isImplicit() && !isa<ParamDecl>(this)))
87898790
return;
87908791

8791-
if (getAttrs().hasAttribute<LazyAttr>()) {
8792-
if (auto *backingVar = getLazyStorageProperty())
8793-
visit(backingVar);
8792+
// The backing storage for a lazy property is not accessible to name lookup.
8793+
// This is not only a matter of hiding implementation details, but also
8794+
// correctness since triggering LazyStoragePropertyRequest currently eagerly
8795+
// requests the interface type for the var, which could result in incorrectly
8796+
// type-checking a lazy local independently of its surrounding closure.
8797+
if (!forNameLookup) {
8798+
if (getAttrs().hasAttribute<LazyAttr>()) {
8799+
if (auto *backingVar = getLazyStorageProperty())
8800+
visit(backingVar);
8801+
}
87948802
}
87958803

87968804
if (getAttrs().hasAttribute<CustomAttr>() || hasImplicitPropertyWrapper()) {

lib/AST/UnqualifiedLookup.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -726,12 +726,13 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume(
726726
bool foundMatch = false;
727727
if (auto *varDecl = dyn_cast<VarDecl>(value)) {
728728
// Check if the name matches any auxiliary decls not in the AST
729-
varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
730-
if (auxiliaryVar->ValueDecl::getName().matchesRef(fullName)) {
731-
value = auxiliaryVar;
732-
foundMatch = true;
733-
}
734-
});
729+
varDecl->visitAuxiliaryVars(
730+
/*forNameLookup*/ true, [&](VarDecl *auxiliaryVar) {
731+
if (auxiliaryVar->ValueDecl::getName().matchesRef(fullName)) {
732+
value = auxiliaryVar;
733+
foundMatch = true;
734+
}
735+
});
735736
}
736737

737738
if (!foundMatch)
@@ -938,13 +939,14 @@ class ASTScopeDeclConsumerForLocalLookup
938939
bool foundMatch = false;
939940
if (auto *varDecl = dyn_cast<VarDecl>(value)) {
940941
// Check if the name matches any auxiliary decls not in the AST
941-
varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
942-
if (name.isSimpleName(auxiliaryVar->getName())
943-
&& hasCorrectABIRole(auxiliaryVar)) {
944-
results.push_back(auxiliaryVar);
945-
foundMatch = true;
946-
}
947-
});
942+
varDecl->visitAuxiliaryVars(
943+
/*forNameLookup*/ true, [&](VarDecl *auxiliaryVar) {
944+
if (name.isSimpleName(auxiliaryVar->getName()) &&
945+
hasCorrectABIRole(auxiliaryVar)) {
946+
results.push_back(auxiliaryVar);
947+
foundMatch = true;
948+
}
949+
});
948950
}
949951

950952
if (!foundMatch && value->getName().matchesRef(name.getFullName())

lib/SILGen/SILGenDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1727,7 +1727,7 @@ void SILGenFunction::visitVarDecl(VarDecl *D) {
17271727
}
17281728

17291729
// Emit lazy and property wrapper backing storage.
1730-
D->visitAuxiliaryDecls([&](VarDecl *var) {
1730+
D->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *var) {
17311731
if (auto *patternBinding = var->getParentPatternBinding())
17321732
visitPatternBindingDecl(patternBinding);
17331733

lib/SILGen/SILGenProlog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,7 @@ class ArgumentInitHelper {
10641064
void emitParam(ParamDecl *PD) {
10651065
// Register any auxiliary declarations for the parameter to be
10661066
// visited later.
1067-
PD->visitAuxiliaryDecls([&](VarDecl *localVar) {
1067+
PD->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *localVar) {
10681068
SGF.LocalAuxiliaryDecls.push_back(localVar);
10691069
});
10701070

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,8 +1224,8 @@ class ASTScopeVisibleDeclConsumer
12241224
// auxiliary variables (unless 'var' is a closure param).
12251225
(void)var->getPropertyWrapperBackingPropertyType();
12261226
}
1227-
var->visitAuxiliaryDecls(
1228-
[&](VarDecl *auxVar) { foundDecl(auxVar); });
1227+
var->visitAuxiliaryVars(/*forNameLookup*/ true,
1228+
[&](VarDecl *auxVar) { foundDecl(auxVar); });
12291229
}
12301230
// NOTE: We don't call Decl::visitAuxiliaryDecls here since peer decls of
12311231
// local decls should not show up in lookup results.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
23882388
checkGlobalIsolation(VD);
23892389

23902390
// Visit auxiliary decls first
2391-
VD->visitAuxiliaryDecls([&](VarDecl *var) {
2391+
VD->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *var) {
23922392
this->visitBoundVariable(var);
23932393
});
23942394

@@ -4125,9 +4125,10 @@ void TypeChecker::checkParameterList(ParameterList *params,
41254125

41264126
if (!param->isInvalid()) {
41274127
auto *SF = owner->getParentSourceFile();
4128-
param->visitAuxiliaryDecls([&](VarDecl *auxiliaryDecl) {
4129-
if (!isa<ParamDecl>(auxiliaryDecl))
4130-
DeclChecker(SF->getASTContext(), SF).visitBoundVariable(auxiliaryDecl);
4128+
auto &ctx = SF->getASTContext();
4129+
param->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *auxVar) {
4130+
if (!isa<ParamDecl>(auxVar))
4131+
DeclChecker(ctx, SF).visitBoundVariable(auxVar);
41314132
});
41324133
}
41334134

lib/Sema/TypeCheckStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ namespace {
153153

154154
// Auxiliary decls need to have their contexts adjusted too.
155155
if (auto *VD = dyn_cast<VarDecl>(D)) {
156-
VD->visitAuxiliaryDecls([&](VarDecl *D) {
156+
VD->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *D) {
157157
D->setDeclContext(ParentDC);
158158
});
159159
}

test/SILGen/lazy_locals.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,9 @@ func lazyLocalInClosure() {
5050
lazy var x = 0
5151
return x
5252
}
53+
// https://github.com/swiftlang/swift/issues/83627
54+
let _: (Int) -> Void = { (arg: _) in
55+
lazy var val = arg
56+
_ = val
57+
}
5358
}

test/decl/var/lazy_properties.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ class LazyVarContainer {
204204
}
205205
}
206206

207+
// The backing storage for lazy locals just isn't exposed through name lookup.
208+
func testLazyLocal() {
209+
lazy var foo = 0
210+
print($__lazy_storage_$_foo) // expected-error {{cannot find '$__lazy_storage_$_foo' in scope}}
211+
}
212+
207213
// Make sure we can still access a synthesized variable with the same name as a lazy storage variable
208214
// i.e. $__lazy_storage_$_{property_name} when using property wrapper where the property name is
209215
// '__lazy_storage_$_{property_name}'.

0 commit comments

Comments
 (0)