Skip to content

Commit 55d0bbe

Browse files
authored
Merge pull request #63566 from xedin/issue-63455-addition
[CSGen] Detect nested out-of-scope variables in recursive declarations
2 parents 99c1fe5 + 8e82f1d commit 55d0bbe

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

lib/Sema/CSGen.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,20 +1354,41 @@ 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(s) could appear the type of
1372+
// a declaration only in diagnostic mode when invalid variable
1373+
// declaration is recursively referenced inside of a multi-statement
1374+
// closure located somewhere within its initializer e.g.:
1375+
// `let x = [<call>] { ... print(x) }`. It happens because the
1376+
// variable assumes the result type of its initializer unless
1377+
// its specified explicitly.
1378+
if (isa<ClosureExpr>(CurDC) && knownType->hasTypeVariable()) {
1379+
if (knownType.findIf([&](Type type) {
1380+
auto *typeVar = type->getAs<TypeVariableType>();
1381+
if (!typeVar || CS.getFixedType(typeVar))
1382+
return false;
1383+
1384+
return !CS.isActiveTypeVariable(typeVar);
1385+
}))
1386+
return invalidateReference();
1387+
}
1388+
13641389
// If the known type has an error, bail out.
13651390
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;
1391+
return invalidateReference();
13711392
}
13721393

13731394
if (!knownType->hasPlaceholder()) {
@@ -1384,10 +1405,7 @@ namespace {
13841405
// (in getTypeOfReference) so we can match non-error param types.
13851406
if (!knownType && E->getDecl()->isInvalid() &&
13861407
!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;
1408+
return invalidateReference();
13911409
}
13921410

13931411
// 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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,3 +650,46 @@ func test_that_closures_are_attempted_in_order() {
650650
return false
651651
}
652652
}
653+
654+
func test_use_of_concrete_params_in_for_condition() {
655+
struct S {
656+
var cond: Bool
657+
}
658+
659+
func test(_: (S) -> Void) {}
660+
661+
test { data in
662+
for i in 0...10 where !data.cond { // Ok
663+
print(i)
664+
}
665+
}
666+
}
667+
668+
// https://github.com/apple/swift/issues/63455
669+
func test_recursive_var_reference_in_multistatement_closure() {
670+
struct MyStruct {
671+
func someMethod() {}
672+
}
673+
674+
func takeClosure(_ x: () -> Void) {}
675+
676+
func test(optionalInt: Int?, themes: MyStruct?) {
677+
takeClosure {
678+
let int = optionalInt { // expected-error {{cannot call value of non-function type 'Int?'}}
679+
print(int)
680+
}
681+
}
682+
683+
takeClosure {
684+
let theme = themes?.someMethod() { // expected-error {{extra trailing closure passed in call}}
685+
_ = theme
686+
}
687+
}
688+
689+
takeClosure {
690+
let theme = themes?.filter({ $0 }) { // expected-error {{value of type 'MyStruct' has no member 'filter'}}
691+
_ = theme
692+
}
693+
}
694+
}
695+
}

0 commit comments

Comments
 (0)