Skip to content

Commit 66c010f

Browse files
committed
[AST] Add bit to VarDecl for isPlaceholderVar
This allows the query to be consistent both during type-checking and after.
2 parents 15dd277 + 8bb956e commit 66c010f

19 files changed

+317
-197
lines changed

include/swift/AST/Decl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
493493
IsStatic : 1
494494
);
495495

496-
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1+1+1,
496+
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1+1+1+1,
497497
/// Encodes whether this is a 'let' binding.
498498
Introducer : 2,
499499

@@ -517,7 +517,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
517517
NoAttachedPropertyWrappers : 1,
518518

519519
/// Whether this variable has no property wrapper auxiliary variables.
520-
NoPropertyWrapperAuxiliaryVariables : 1
520+
NoPropertyWrapperAuxiliaryVariables : 1,
521+
522+
/// Whether this variable is a placeholder that is introducing during
523+
/// type-checking that has its type inferred from its use.
524+
IsPlaceholderVar : 1
521525
);
522526

523527
SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits,
@@ -6767,6 +6771,15 @@ class VarDecl : public AbstractStorageDecl {
67676771
Bits.VarDecl.IsLazyStorageProperty = IsLazyStorage;
67686772
}
67696773

6774+
/// Whether this variable is a placeholder that is introducing during
6775+
/// type-checking that has its type inferred from its use.
6776+
bool isPlaceholderVar() const {
6777+
return Bits.VarDecl.IsPlaceholderVar;
6778+
}
6779+
void setIsPlaceholderVar() {
6780+
Bits.VarDecl.IsPlaceholderVar = true;
6781+
}
6782+
67706783
/// Retrieve the backing storage property for a lazy property.
67716784
VarDecl *getLazyStorageProperty() const;
67726785

lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8044,6 +8044,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer,
80448044
Bits.VarDecl.IsTopLevelGlobal = false;
80458045
Bits.VarDecl.NoAttachedPropertyWrappers = false;
80468046
Bits.VarDecl.NoPropertyWrapperAuxiliaryVariables = false;
8047+
Bits.VarDecl.IsPlaceholderVar = false;
80478048
}
80488049

80498050
Type VarDecl::getTypeInContext() const {

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class ResultBuilderTransform
124124
SmallVectorImpl<ASTNode> &container,
125125
Type type = Type(), Expr *initExpr = nullptr) {
126126
auto *var = builder.buildVar(loc);
127+
var->setIsPlaceholderVar();
127128
Pattern *placeholder = TypedPattern::createImplicit(
128129
ctx, NamedPattern::createImplicit(ctx, var),
129130
type ? type : PlaceholderType::get(ctx, var));

lib/Sema/CSSyntacticElement.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,20 +2783,13 @@ void ConjunctionElement::findReferencedVariables(
27832783

27842784
Type constraints::isPlaceholderVar(PatternBindingDecl *PB) {
27852785
auto *var = PB->getSingleVar();
2786-
if (!var)
2787-
return Type();
2788-
2789-
if (!var->getName().hasDollarPrefix())
2786+
if (!var || !var->isPlaceholderVar())
27902787
return Type();
27912788

27922789
auto *pattern = PB->getPattern(0);
27932790
auto *typedPattern = dyn_cast<TypedPattern>(pattern);
27942791
if (!typedPattern || !typedPattern->hasType())
27952792
return Type();
27962793

2797-
auto type = typedPattern->getType();
2798-
if (!type->hasPlaceholder())
2799-
return Type();
2800-
2801-
return type;
2794+
return typedPattern->getType();
28022795
}

lib/Sema/PreCheckTarget.cpp

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

395395
static bool isValidForwardReference(ValueDecl *D, DeclContext *DC,
396-
ValueDecl **localDeclAfterUse) {
397-
*localDeclAfterUse = nullptr;
398-
399-
// References to variables injected by lldb are always valid.
400-
if (isa<VarDecl>(D) && cast<VarDecl>(D)->isDebuggerVar())
396+
ValueDecl *&localDeclAfterUse) {
397+
// Only VarDecls require declaration before use.
398+
auto *VD = dyn_cast<VarDecl>(D);
399+
if (!VD)
401400
return true;
402401

403-
// If we find something in the current context, it must be a forward
404-
// reference, because otherwise if it was in scope, it would have
405-
// been returned by the call to ASTScope::lookupLocalDecls() above.
406-
if (D->getDeclContext()->isLocalContext()) {
407-
do {
408-
if (D->getDeclContext() == DC) {
409-
*localDeclAfterUse = D;
410-
return false;
411-
}
402+
// Non-local and variables injected by lldb are always valid.
403+
auto *varDC = VD->getDeclContext();
404+
if (!varDC->isLocalContext() || VD->isDebuggerVar())
405+
return true;
412406

413-
// If we're inside of a 'defer' context, walk up to the parent
414-
// and check again. We don't want 'defer' bodies to forward
415-
// reference bindings in the immediate outer scope.
416-
} while (isa<FuncDecl>(DC) &&
417-
cast<FuncDecl>(DC)->isDeferBody() &&
418-
(DC = DC->getParent()));
407+
while (true) {
408+
if (varDC == DC) {
409+
localDeclAfterUse = VD;
410+
return false;
411+
}
412+
if (isa<AbstractClosureExpr>(DC) ||
413+
(isa<FuncDecl>(DC) && cast<FuncDecl>(DC)->isDeferBody())) {
414+
DC = DC->getParent();
415+
continue;
416+
}
417+
break;
419418
}
420419
return true;
421420
}
@@ -587,12 +586,11 @@ static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC,
587586
Lookup = TypeChecker::lookupUnqualified(DC, LookupName, Loc, lookupOptions);
588587

589588
ValueDecl *localDeclAfterUse = nullptr;
590-
AllDeclRefs =
591-
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
592-
/*breakOnMember=*/true, ResultValues,
593-
[&](ValueDecl *D) {
594-
return isValidForwardReference(D, DC, &localDeclAfterUse);
595-
});
589+
AllDeclRefs = findNonMembers(
590+
Lookup.innerResults(), UDRE->getRefKind(),
591+
/*breakOnMember=*/true, ResultValues, [&](ValueDecl *D) {
592+
return isValidForwardReference(D, DC, localDeclAfterUse);
593+
});
596594

597595
// If local declaration after use is found, check outer results for
598596
// better matching candidates.
@@ -609,12 +607,11 @@ static Expr *resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC,
609607
Lookup.shiftDownResults();
610608
ResultValues.clear();
611609
localDeclAfterUse = nullptr;
612-
AllDeclRefs =
613-
findNonMembers(Lookup.innerResults(), UDRE->getRefKind(),
614-
/*breakOnMember=*/true, ResultValues,
615-
[&](ValueDecl *D) {
616-
return isValidForwardReference(D, DC, &localDeclAfterUse);
617-
});
610+
AllDeclRefs = findNonMembers(
611+
Lookup.innerResults(), UDRE->getRefKind(),
612+
/*breakOnMember=*/true, ResultValues, [&](ValueDecl *D) {
613+
return isValidForwardReference(D, DC, localDeclAfterUse);
614+
});
618615
}
619616
}
620617
}

test/NameLookup/name_lookup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,8 @@ struct PatternBindingWithTwoVars3 { var x = y, y = x }
643643

644644
// https://github.com/apple/swift/issues/51518
645645
do {
646-
let closure1 = { closure2() } // expected-error {{circular reference}} expected-note {{through reference here}}
647-
let closure2 = { closure1() } // expected-note {{through reference here}} expected-note {{through reference here}}
646+
let closure1 = { closure2() } // expected-error {{use of local variable 'closure2' before its declaration}}
647+
let closure2 = { closure1() } // expected-note {{'closure2' declared here}}
648648
}
649649

650650
func color(with value: Int) -> Int {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-typecheck-verify-swift -parse-as-library
3+
4+
func testLocal() {
5+
// The first `y` here is considered the inner result.
6+
do {
7+
let y = ""
8+
do {
9+
let _: String = y
10+
let y = 0
11+
_ = y
12+
}
13+
}
14+
do {
15+
let y = ""
16+
do {
17+
_ = {
18+
let _: String = y
19+
}
20+
let y = 0
21+
_ = y
22+
}
23+
}
24+
do {
25+
let y = ""
26+
_ = {
27+
_ = {
28+
let _: String = y
29+
}
30+
let y = 0
31+
_ = y
32+
}
33+
}
34+
do {
35+
let y = ""
36+
func bar() {
37+
_ = {
38+
let _: String = y
39+
}
40+
let y = 0
41+
_ = y
42+
}
43+
}
44+
}
45+
46+
let topLevelString = ""
47+
48+
func testTopLevel() {
49+
// Here 'topLevelString' is now an outer result.
50+
do {
51+
let _: String = topLevelString
52+
let topLevelString = 0
53+
_ = topLevelString
54+
}
55+
do {
56+
_ = {
57+
let _: String = topLevelString
58+
}
59+
let topLevelString = 0
60+
_ = topLevelString
61+
}
62+
_ = {
63+
_ = {
64+
let _: String = topLevelString
65+
}
66+
let topLevelString = 0
67+
_ = topLevelString
68+
}
69+
func bar() {
70+
_ = {
71+
let _: String = topLevelString
72+
}
73+
let topLevelString = 0
74+
_ = topLevelString
75+
}
76+
}

0 commit comments

Comments
 (0)