Skip to content

Commit 2e67c13

Browse files
committed
ASTScope: Rework ConditionalClauseScopes
The top-level scope for a conditional clause with a pattern is now ConditionalClausePatternUseScope, which introduces the pattern's bindings. Since the patterns are not visible in their own initializer, a new ConditionalClauseInitializerScope is used for the initializer. While it is nested inside of the ConditionalClausePatternUseScope, it's lookup parent skips one level, giving us the desired behavior.
1 parent 66873b9 commit 2e67c13

8 files changed

+146
-146
lines changed

include/swift/AST/ASTScope.h

+31-49
Original file line numberDiff line numberDiff line change
@@ -1082,63 +1082,49 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope {
10821082
bool lookupLocalsOrMembers(DeclConsumer) const override;
10831083
};
10841084

1085-
/// The scope introduced by a conditional clause in an if/guard/while
1086-
/// statement.
1087-
/// Since there may be more than one "let foo = ..." in (e.g.) an "if",
1088-
/// we allocate a matrushka of these.
1089-
class ConditionalClauseScope final : public ASTScopeImpl {
1085+
/// The scope introduced by a conditional clause initializer in an
1086+
/// if/while/guard statement.
1087+
class ConditionalClauseInitializerScope final : public ASTScopeImpl {
10901088
public:
1091-
LabeledConditionalStmt *const stmt;
1092-
const unsigned index;
1093-
const SourceLoc endLoc; // cannot get it from the stmt
1094-
1095-
ConditionalClauseScope(LabeledConditionalStmt *stmt, unsigned index,
1096-
SourceLoc endLoc)
1097-
: stmt(stmt), index(index), endLoc(endLoc) {}
1098-
1099-
virtual ~ConditionalClauseScope() {}
1100-
1101-
protected:
1102-
ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override;
1089+
Expr *const initializer;
1090+
const SourceRange bodyRange;
11031091

1104-
private:
1105-
AnnotatedInsertionPoint
1106-
expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &);
1107-
1108-
public:
1109-
std::string getClassName() const override;
1110-
1111-
protected:
1112-
void printSpecifics(llvm::raw_ostream &out) const override;
1092+
ConditionalClauseInitializerScope(Expr *initializer)
1093+
: initializer(initializer) {}
11131094

1114-
public:
1095+
virtual ~ConditionalClauseInitializerScope() {}
11151096
SourceRange
11161097
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
1098+
std::string getClassName() const override;
11171099

11181100
private:
1119-
ArrayRef<StmtConditionElement> getCond() const;
1120-
const StmtConditionElement &getStmtConditionElement() const;
1101+
void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &);
11211102

11221103
protected:
1123-
bool isLabeledStmtLookupTerminator() const override;
1104+
ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override;
1105+
NullablePtr<const ASTScopeImpl> getLookupParent() const override;
11241106
};
11251107

11261108
/// If, while, & guard statements all start with a conditional clause, then some
11271109
/// later part of the statement, (then, body, or after the guard) circumvents
11281110
/// the normal lookup rule to pass the lookup scope into the deepest conditional
11291111
/// clause.
11301112
class ConditionalClausePatternUseScope final : public ASTScopeImpl {
1131-
Pattern *const pattern;
1132-
const SourceLoc startLoc;
1113+
StmtConditionElement sec;
1114+
SourceLoc endLoc;
11331115

11341116
public:
1135-
ConditionalClausePatternUseScope(Pattern *pattern, SourceLoc startLoc)
1136-
: pattern(pattern), startLoc(startLoc) {}
1117+
ConditionalClausePatternUseScope(StmtConditionElement sec, SourceLoc endLoc)
1118+
: sec(sec), endLoc(endLoc) {}
11371119

11381120
SourceRange
11391121
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
11401122
std::string getClassName() const override;
11411123

1124+
private:
1125+
AnnotatedInsertionPoint
1126+
expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &);
1127+
11421128
protected:
11431129
ASTScopeImpl *expandSpecifically(ScopeCreator &) override;
11441130
bool lookupLocalsOrMembers(DeclConsumer) const override;
@@ -1350,7 +1336,7 @@ class LabeledConditionalStmtScope : public AbstractStmtScope {
13501336
protected:
13511337
/// Return the lookupParent required to search these.
13521338
ASTScopeImpl *createNestedConditionalClauseScopes(ScopeCreator &,
1353-
const Stmt *afterConds);
1339+
SourceLoc);
13541340
};
13551341

13561342
class IfStmtScope final : public LabeledConditionalStmtScope {
@@ -1408,28 +1394,24 @@ class GuardStmtScope final : public LabeledConditionalStmtScope {
14081394
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
14091395
};
14101396

1411-
/// A scope after a guard statement that follows lookups into the conditions
1412-
/// Also for:
1413-
/// The insertion point of the last statement of an active clause in an #if
1414-
/// must be the lookup parent
1415-
/// of any following scopes. But the active clause may not be the last clause.
1416-
/// In short, this is another case where the lookup parent cannot follow the same
1417-
/// nesting as the source order. IfConfigUseScope implements this twist. It
1418-
/// follows the IfConfig, wraps all subsequent scopes, and redirects the lookup.
1419-
class LookupParentDiversionScope final : public ASTScopeImpl {
1397+
/// A scope for the body of a guard statement. Lookups from the body must
1398+
/// skip the parent scopes for introducing pattern bindings, since they're
1399+
/// not visible in the guard body, only after the body ends.
1400+
class GuardStmtBodyScope final : public ASTScopeImpl {
14201401
public:
14211402
ASTScopeImpl *const lookupParent;
1422-
const SourceLoc startLoc;
1423-
const SourceLoc endLoc;
1403+
BraceStmt *const body;
14241404

1425-
LookupParentDiversionScope(ASTScopeImpl *lookupParent,
1426-
SourceLoc startLoc, SourceLoc endLoc)
1427-
: lookupParent(lookupParent), startLoc(startLoc), endLoc(endLoc) {}
1405+
GuardStmtBodyScope(ASTScopeImpl *lookupParent, BraceStmt *body)
1406+
: lookupParent(lookupParent), body(body) {}
14281407

14291408
SourceRange
14301409
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
14311410
std::string getClassName() const override;
14321411

1412+
private:
1413+
void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &);
1414+
14331415
protected:
14341416
ASTScopeImpl *expandSpecifically(ScopeCreator &) override;
14351417
NullablePtr<const ASTScopeImpl> getLookupParent() const override {

lib/AST/ASTScope.cpp

+2-11
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ DEFINE_GET_CLASS_NAME(DefaultArgumentInitializerScope)
137137
DEFINE_GET_CLASS_NAME(AttachedPropertyWrapperScope)
138138
DEFINE_GET_CLASS_NAME(PatternEntryDeclScope)
139139
DEFINE_GET_CLASS_NAME(PatternEntryInitializerScope)
140-
DEFINE_GET_CLASS_NAME(ConditionalClauseScope)
141140
DEFINE_GET_CLASS_NAME(ConditionalClausePatternUseScope)
141+
DEFINE_GET_CLASS_NAME(ConditionalClauseInitializerScope)
142142
DEFINE_GET_CLASS_NAME(CaptureListScope)
143143
DEFINE_GET_CLASS_NAME(ClosureParametersScope)
144144
DEFINE_GET_CLASS_NAME(TopLevelCodeScope)
@@ -149,7 +149,7 @@ DEFINE_GET_CLASS_NAME(EnumElementScope)
149149
DEFINE_GET_CLASS_NAME(IfStmtScope)
150150
DEFINE_GET_CLASS_NAME(WhileStmtScope)
151151
DEFINE_GET_CLASS_NAME(GuardStmtScope)
152-
DEFINE_GET_CLASS_NAME(LookupParentDiversionScope)
152+
DEFINE_GET_CLASS_NAME(GuardStmtBodyScope)
153153
DEFINE_GET_CLASS_NAME(RepeatWhileScope)
154154
DEFINE_GET_CLASS_NAME(DoStmtScope)
155155
DEFINE_GET_CLASS_NAME(DoCatchStmtScope)
@@ -199,15 +199,6 @@ void ASTScopeImpl::postOrderDo(function_ref<void(ASTScopeImpl *)> fn) {
199199
fn(this);
200200
}
201201

202-
ArrayRef<StmtConditionElement> ConditionalClauseScope::getCond() const {
203-
return stmt->getCond();
204-
}
205-
206-
const StmtConditionElement &
207-
ConditionalClauseScope::getStmtConditionElement() const {
208-
return getCond()[index];
209-
}
210-
211202
unsigned ASTScopeImpl::countDescendants() const {
212203
unsigned count = 0;
213204
const_cast<ASTScopeImpl *>(this)->preOrderDo(

lib/AST/ASTScopeCreation.cpp

+76-41
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,9 @@ class NodeAdder
687687

688688
NullablePtr<ASTScopeImpl> visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p,
689689
ScopeCreator &scopeCreator) {
690+
if (bs->empty())
691+
return p;
692+
690693
SmallVector<ValueDecl *, 2> localFuncsAndTypes;
691694
SmallVector<VarDecl *, 2> localVars;
692695

@@ -941,24 +944,26 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) {
941944
ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &) { return this; }
942945

943946
CREATES_NEW_INSERTION_POINT(ASTSourceFileScope)
944-
CREATES_NEW_INSERTION_POINT(ConditionalClauseScope)
945947
CREATES_NEW_INSERTION_POINT(GuardStmtScope)
946948
CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope)
947949
CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope)
948950
CREATES_NEW_INSERTION_POINT(BraceStmtScope)
949951
CREATES_NEW_INSERTION_POINT(TopLevelCodeScope)
952+
CREATES_NEW_INSERTION_POINT(ConditionalClausePatternUseScope)
950953

951954
NO_NEW_INSERTION_POINT(FunctionBodyScope)
952955
NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope)
953956
NO_NEW_INSERTION_POINT(AttachedPropertyWrapperScope)
954957
NO_NEW_INSERTION_POINT(EnumElementScope)
958+
NO_NEW_INSERTION_POINT(GuardStmtBodyScope)
955959
NO_NEW_INSERTION_POINT(ParameterListScope)
956960
NO_NEW_INSERTION_POINT(PatternEntryInitializerScope)
957961

958962
NO_NEW_INSERTION_POINT(CaptureListScope)
959963
NO_NEW_INSERTION_POINT(CaseStmtScope)
960964
NO_NEW_INSERTION_POINT(CaseLabelItemScope)
961965
NO_NEW_INSERTION_POINT(CaseStmtBodyScope)
966+
NO_NEW_INSERTION_POINT(ConditionalClauseInitializerScope)
962967
NO_NEW_INSERTION_POINT(ClosureParametersScope)
963968
NO_NEW_INSERTION_POINT(DefaultArgumentInitializerScope)
964969
NO_NEW_INSERTION_POINT(DoStmtScope)
@@ -974,8 +979,6 @@ NO_NEW_INSERTION_POINT(WhileStmtScope)
974979
NO_EXPANSION(GenericParamScope)
975980
NO_EXPANSION(SpecializeAttributeScope)
976981
NO_EXPANSION(DifferentiableAttributeScope)
977-
NO_EXPANSION(ConditionalClausePatternUseScope)
978-
NO_EXPANSION(LookupParentDiversionScope)
979982

980983
#undef CREATES_NEW_INSERTION_POINT
981984
#undef NO_NEW_INSERTION_POINT
@@ -1057,42 +1060,49 @@ PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
10571060
this);
10581061
}
10591062

1063+
10601064
AnnotatedInsertionPoint
1061-
ConditionalClauseScope::expandAScopeThatCreatesANewInsertionPoint(
1065+
ConditionalClausePatternUseScope::expandAScopeThatCreatesANewInsertionPoint(
10621066
ScopeCreator &scopeCreator) {
1063-
const StmtConditionElement &sec = getStmtConditionElement();
1064-
switch (sec.getKind()) {
1065-
case StmtConditionElement::CK_Availability:
1066-
return {this, "No introduced variables"};
1067-
case StmtConditionElement::CK_Boolean:
1068-
scopeCreator.addToScopeTree(sec.getBoolean(), this);
1069-
return {this, "No introduced variables"};
1070-
case StmtConditionElement::CK_PatternBinding:
1071-
scopeCreator.addToScopeTree(sec.getInitializer(), this);
1072-
auto *const ccPatternUseScope =
1073-
scopeCreator.constructExpandAndInsertUncheckable<
1074-
ConditionalClausePatternUseScope>(this, sec.getPattern(), endLoc);
1075-
return {ccPatternUseScope,
1076-
"Succeeding code must be in scope of conditional variables"};
1077-
}
1078-
ASTScope_unreachable("Unhandled StmtConditionKind in switch");
1067+
auto *initializer = sec.getInitializer();
1068+
if (!isa<ErrorExpr>(initializer)) {
1069+
scopeCreator
1070+
.constructExpandAndInsertUncheckable<ConditionalClauseInitializerScope>(
1071+
this, initializer);
1072+
}
1073+
1074+
return {this,
1075+
"Succeeding code must be in scope of conditional clause pattern bindings"};
1076+
}
1077+
1078+
void
1079+
ConditionalClauseInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
1080+
ScopeCreator &scopeCreator) {
1081+
scopeCreator.addToScopeTree(ASTNode(initializer), this);
1082+
}
1083+
1084+
void
1085+
GuardStmtBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &
1086+
scopeCreator) {
1087+
scopeCreator.addToScopeTree(ASTNode(body), this);
10791088
}
10801089

10811090
AnnotatedInsertionPoint
10821091
GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &
10831092
scopeCreator) {
1084-
10851093
ASTScopeImpl *conditionLookupParent =
1086-
createNestedConditionalClauseScopes(scopeCreator, stmt->getBody());
1094+
createNestedConditionalClauseScopes(scopeCreator, endLoc);
1095+
10871096
// Add a child for the 'guard' body, which always exits.
1088-
// Parent is whole guard stmt scope, NOT the cond scopes
1089-
scopeCreator.addToScopeTree(stmt->getBody(), this);
1097+
// The lookup parent is whole guard stmt scope, NOT the cond scopes
1098+
auto *body = stmt->getBody();
1099+
if (!body->empty()) {
1100+
scopeCreator
1101+
.constructExpandAndInsertUncheckable<GuardStmtBodyScope>(
1102+
conditionLookupParent, this, stmt->getBody());
1103+
}
10901104

1091-
auto *const lookupParentDiversionScope =
1092-
scopeCreator
1093-
.constructExpandAndInsertUncheckable<LookupParentDiversionScope>(
1094-
this, conditionLookupParent, stmt->getEndLoc(), endLoc);
1095-
return {lookupParentDiversionScope,
1105+
return {conditionLookupParent,
10961106
"Succeeding code must be in scope of guard variables"};
10971107
}
10981108

@@ -1188,20 +1198,34 @@ void FunctionBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
11881198

11891199
void IfStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
11901200
ScopeCreator &scopeCreator) {
1201+
auto *thenStmt = stmt->getThenStmt();
1202+
auto *elseStmt = stmt->getElseStmt();
1203+
1204+
SourceLoc endLoc = thenStmt->getEndLoc();
11911205
ASTScopeImpl *insertionPoint =
1192-
createNestedConditionalClauseScopes(scopeCreator, stmt->getThenStmt());
1206+
createNestedConditionalClauseScopes(scopeCreator, endLoc);
11931207

11941208
// The 'then' branch
1195-
scopeCreator.addToScopeTree(stmt->getThenStmt(), insertionPoint);
1209+
scopeCreator.addToScopeTree(thenStmt, insertionPoint);
1210+
1211+
// Result builders can add an 'else' block consisting entirely of
1212+
// implicit expressions. In this case, the end location of the
1213+
// 'then' block is equal to the start location of the 'else'
1214+
// block, and the 'else' block source range is empty.
1215+
if (elseStmt &&
1216+
thenStmt->getEndLoc() == elseStmt->getStartLoc() &&
1217+
elseStmt->getStartLoc() == elseStmt->getEndLoc())
1218+
return;
11961219

11971220
// Add the 'else' branch, if needed.
1198-
scopeCreator.addToScopeTree(stmt->getElseStmt(), this);
1221+
scopeCreator.addToScopeTree(elseStmt, this);
11991222
}
12001223

12011224
void WhileStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
12021225
ScopeCreator &scopeCreator) {
1226+
SourceLoc endLoc = stmt->getBody()->getEndLoc();
12031227
ASTScopeImpl *insertionPoint =
1204-
createNestedConditionalClauseScopes(scopeCreator, stmt->getBody());
1228+
createNestedConditionalClauseScopes(scopeCreator, endLoc);
12051229
scopeCreator.addToScopeTree(stmt->getBody(), insertionPoint);
12061230
}
12071231

@@ -1266,8 +1290,10 @@ void CaseStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
12661290
}
12671291
}
12681292

1269-
scopeCreator.constructExpandAndInsertUncheckable<CaseStmtBodyScope>(
1270-
this, stmt);
1293+
if (!stmt->getBody()->empty()) {
1294+
scopeCreator.constructExpandAndInsertUncheckable<CaseStmtBodyScope>(
1295+
this, stmt);
1296+
}
12711297
}
12721298

12731299
void CaseLabelItemScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
@@ -1409,14 +1435,23 @@ TypeAliasScope::createTrailingWhereClauseScope(ASTScopeImpl *parent,
14091435
#pragma mark misc
14101436

14111437
ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes(
1412-
ScopeCreator &scopeCreator, const Stmt *const afterConds) {
1438+
ScopeCreator &scopeCreator, SourceLoc endLoc) {
14131439
auto *stmt = getLabeledConditionalStmt();
14141440
ASTScopeImpl *insertionPoint = this;
1415-
for (unsigned i = 0; i < stmt->getCond().size(); ++i) {
1416-
insertionPoint =
1417-
scopeCreator
1418-
.constructExpandAndInsertUncheckable<ConditionalClauseScope>(
1419-
insertionPoint, stmt, i, afterConds->getStartLoc());
1441+
for (auto &sec : stmt->getCond()) {
1442+
switch (sec.getKind()) {
1443+
case StmtConditionElement::CK_Availability:
1444+
break;
1445+
case StmtConditionElement::CK_Boolean:
1446+
scopeCreator.addToScopeTree(sec.getBoolean(), insertionPoint);
1447+
break;
1448+
case StmtConditionElement::CK_PatternBinding:
1449+
insertionPoint =
1450+
scopeCreator.constructExpandAndInsertUncheckable<
1451+
ConditionalClausePatternUseScope>(
1452+
insertionPoint, sec, endLoc);
1453+
break;
1454+
}
14201455
}
14211456
return insertionPoint;
14221457
}

0 commit comments

Comments
 (0)