Skip to content

Commit db55070

Browse files
committed
[CSGen] Handle recursive use of variable declarations
It's possible for out-of-scope type variable to be the type of declaration if such declaration is recursively referenced in the body of a closure located in its initializer expression. In such cases type of the variable declaration cannot be connected to the closure because its not known in advance (determined by the initializer itself). Resolves: #63455
1 parent 47c3ef5 commit db55070

File tree

3 files changed

+33
-10
lines changed

3 files changed

+33
-10
lines changed

lib/Sema/CSGen.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,20 +1354,33 @@ namespace {
13541354
Type visitDeclRefExpr(DeclRefExpr *E) {
13551355
auto locator = CS.getConstraintLocator(E);
13561356

1357+
auto invalidateReference = [&]() -> Type {
1358+
auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole);
1359+
(void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator));
1360+
CS.setType(E, hole);
1361+
return hole;
1362+
};
1363+
13571364
Type knownType;
13581365
if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) {
13591366
knownType = CS.getTypeIfAvailable(VD);
13601367
if (!knownType)
13611368
knownType = CS.getVarType(VD);
13621369

13631370
if (knownType) {
1371+
// An out-of-scope type variable could be a type of a declaration
1372+
// only in diagnostic mode when invalid variable declaration is
1373+
// recursively referenced inside of a multi-statement closure
1374+
// located somewhere within its initializer e.g.:
1375+
// `let x = [<call>] { ... print(x) }`
1376+
if (auto *typeVar = knownType->getAs<TypeVariableType>()) {
1377+
if (!CS.isActiveTypeVariable(typeVar))
1378+
return invalidateReference();
1379+
}
1380+
13641381
// If the known type has an error, bail out.
13651382
if (knownType->hasError()) {
1366-
auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole);
1367-
(void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator));
1368-
if (!CS.hasType(E))
1369-
CS.setType(E, hole);
1370-
return hole;
1383+
return invalidateReference();
13711384
}
13721385

13731386
if (!knownType->hasPlaceholder()) {
@@ -1384,10 +1397,7 @@ namespace {
13841397
// (in getTypeOfReference) so we can match non-error param types.
13851398
if (!knownType && E->getDecl()->isInvalid() &&
13861399
!CS.isForCodeCompletion()) {
1387-
auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole);
1388-
(void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator));
1389-
CS.setType(E, hole);
1390-
return hole;
1400+
return invalidateReference();
13911401
}
13921402

13931403
// Create an overload choice referencing this declaration and immediately

lib/Sema/CSSyntacticElement.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class TypeVariableRefFinder : public ASTWalker {
7474
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
7575
auto *decl = DRE->getDecl();
7676

77-
if (auto type = CS.getTypeIfAvailable(DRE->getDecl())) {
77+
if (auto type = CS.getTypeIfAvailable(decl)) {
7878
auto &ctx = CS.getASTContext();
7979
// If this is not one of the closure parameters which
8080
// is inferrable from the body, let's replace type

test/expr/closure/multi_statement.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,3 +650,16 @@ func test_that_closures_are_attempted_in_order() {
650650
return false
651651
}
652652
}
653+
654+
// https://github.com/apple/swift/issues/63455
655+
func test_recursive_var_reference_in_multistatement_closure() {
656+
func takeClosure(_ x: () -> Void) {}
657+
658+
func test(optionalInt: Int?) {
659+
takeClosure {
660+
let int = optionalInt { // expected-error {{cannot call value of non-function type 'Int?'}}
661+
print(int)
662+
}
663+
}
664+
}
665+
}

0 commit comments

Comments
 (0)