Skip to content

Commit f2e8a26

Browse files
authored
Merge pull request #33116 from DougGregor/captures-in-constraint-system
[Constraint solver] Type check captures as part of the constraint system
2 parents 0f9f104 + 05c4cee commit f2e8a26

File tree

8 files changed

+77
-19
lines changed

8 files changed

+77
-19
lines changed

include/swift/AST/ASTWalker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ class ASTWalker {
229229
/// TapExpr.
230230
virtual bool shouldWalkIntoTapExpression() { return true; }
231231

232+
/// This method configures whether the walker should visit the capture
233+
/// initializer expressions within a capture list directly, rather than
234+
/// walking the declarations.
235+
virtual bool shouldWalkCaptureInitializerExpressions() { return false; }
236+
232237
/// This method configures whether the walker should exhibit the legacy
233238
/// behavior where accessors appear as peers of their storage, rather
234239
/// than children nested inside of it.

lib/AST/ASTWalker.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,16 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
803803

804804
Expr *visitCaptureListExpr(CaptureListExpr *expr) {
805805
for (auto c : expr->getCaptureList()) {
806-
if (doIt(c.Var) || doIt(c.Init))
806+
if (Walker.shouldWalkCaptureInitializerExpressions()) {
807+
for (auto entryIdx : range(c.Init->getNumPatternEntries())) {
808+
if (auto newInit = doIt(c.Init->getInit(entryIdx)))
809+
c.Init->setInit(entryIdx, newInit);
810+
else
811+
return nullptr;
812+
}
813+
} else if (doIt(c.Var) || doIt(c.Init)) {
807814
return nullptr;
815+
}
808816
}
809817

810818
ClosureExpr *body = expr->getClosureBody();

lib/AST/Expr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,8 +1968,15 @@ bool ClosureExpr::hasEmptyBody() const {
19681968
}
19691969

19701970
bool ClosureExpr::capturesSelfEnablingImplictSelf() const {
1971-
if (auto *VD = getCapturedSelfDecl())
1972-
return VD->isSelfParamCapture() && !VD->getType()->is<WeakStorageType>();
1971+
if (auto *VD = getCapturedSelfDecl()) {
1972+
if (!VD->isSelfParamCapture())
1973+
return false;
1974+
1975+
if (auto *attr = VD->getAttrs().getAttribute<ReferenceOwnershipAttr>())
1976+
return attr->get() != ReferenceOwnership::Weak;
1977+
1978+
return true;
1979+
}
19731980
return false;
19741981
}
19751982

lib/Sema/CSApply.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7649,6 +7649,13 @@ namespace {
76497649
TapsToTypeCheck.push_back(std::make_pair(tap, Rewriter.dc));
76507650
}
76517651

7652+
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
7653+
// Rewrite captures.
7654+
for (const auto &capture : captureList->getCaptureList()) {
7655+
(void)rewriteTarget(SolutionApplicationTarget(capture.Init));
7656+
}
7657+
}
7658+
76527659
Rewriter.walkToExprPre(expr);
76537660
return { true, expr };
76547661
}

lib/Sema/CSGen.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,6 +2783,8 @@ namespace {
27832783

27842784
CollectVarRefs(ConstraintSystem &cs) : cs(cs) { }
27852785

2786+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
2787+
27862788
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
27872789
// If there are any error expressions in this closure
27882790
// it wouldn't be possible to infer its type.
@@ -3729,6 +3731,16 @@ namespace {
37293731
}
37303732
}
37313733

3734+
// Generate constraints for each of the entries in the capture list.
3735+
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
3736+
auto &CS = CG.getConstraintSystem();
3737+
for (const auto &capture : captureList->getCaptureList()) {
3738+
SolutionApplicationTarget target(capture.Init);
3739+
if (CS.generateConstraints(target, FreeTypeVariableBinding::Disallow))
3740+
return {false, nullptr};
3741+
}
3742+
}
3743+
37323744
// Both multi- and single-statement closures now behave the same way
37333745
// when it comes to constraint generation.
37343746
if (auto closure = dyn_cast<ClosureExpr>(expr)) {

lib/Sema/MiscDiagnostics.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
9696
return false;
9797
}
9898

99+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
100+
99101
bool shouldWalkIntoTapExpression() override { return false; }
100102

101103
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
@@ -1332,6 +1334,8 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) {
13321334
return false;
13331335
}
13341336

1337+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
1338+
13351339
bool shouldWalkIntoTapExpression() override { return false; }
13361340

13371341
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
@@ -1505,6 +1509,8 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15051509
return false;
15061510
}
15071511

1512+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
1513+
15081514
bool shouldWalkIntoTapExpression() override { return false; }
15091515

15101516
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
@@ -1515,7 +1521,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15151521
Closures.push_back(CE);
15161522
}
15171523

1518-
15191524
// If we aren't in a closure, no diagnostics will be produced.
15201525
if (Closures.size() == 0)
15211526
return { true, E };
@@ -3332,6 +3337,8 @@ static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) {
33323337
return false;
33333338
}
33343339

3340+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
3341+
33353342
bool shouldWalkIntoTapExpression() override { return false; }
33363343

33373344
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
@@ -3482,6 +3489,8 @@ class ObjCSelectorWalker : public ASTWalker {
34823489
return false;
34833490
}
34843491

3492+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
3493+
34853494
bool shouldWalkIntoTapExpression() override { return false; }
34863495

34873496
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
@@ -4222,6 +4231,8 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
42224231
return false;
42234232
}
42244233

4234+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
4235+
42254236
bool shouldWalkIntoTapExpression() override { return false; }
42264237

42274238
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
@@ -4298,6 +4309,8 @@ static void diagnoseDeprecatedWritableKeyPath(const Expr *E,
42984309
return false;
42994310
}
43004311

4312+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
4313+
43014314
bool shouldWalkIntoTapExpression() override { return false; }
43024315

43034316
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,8 @@ namespace {
10271027

10281028
bool walkToClosureExprPre(ClosureExpr *expr);
10291029

1030+
bool shouldWalkCaptureInitializerExpressions() override { return true; }
1031+
10301032
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
10311033
// If this is a call, record the argument expression.
10321034
if (auto call = dyn_cast<ApplyExpr>(expr)) {
@@ -1076,21 +1078,6 @@ namespace {
10761078
return std::make_pair(recursive, expr);
10771079
};
10781080

1079-
// For capture lists, we typecheck the decls they contain.
1080-
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
1081-
// Validate the capture list.
1082-
for (auto capture : captureList->getCaptureList()) {
1083-
TypeChecker::typeCheckDecl(capture.Init);
1084-
TypeChecker::typeCheckDecl(capture.Var);
1085-
}
1086-
1087-
// Since closure expression is contained by capture list
1088-
// let's handle it directly to avoid walking into capture
1089-
// list itself.
1090-
captureList->getClosureBody()->walk(*this);
1091-
return finish(false, expr);
1092-
}
1093-
10941081
// For closures, type-check the patterns and result type as written,
10951082
// but do not walk into the body. That will be type-checked after
10961083
// we've determine the complete function type.

test/expr/closure/closures.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,22 @@ var qux1: (() -> Void)? = {}
504504

505505
SR9839(qux1!)
506506
SR9839(forceUnwrap(qux1))
507+
508+
// rdar://problem/65155671 - crash referencing parameter of outer closure
509+
func rdar65155671(x: Int) {
510+
{ a in
511+
_ = { [a] in a }
512+
}(x)
513+
}
514+
515+
func sr3186<T, U>(_ f: (@escaping (@escaping (T) -> U) -> ((T) -> U))) -> ((T) -> U) {
516+
return { x in return f(sr3186(f))(x) }
517+
}
518+
519+
class SR3186 {
520+
init() {
521+
// expected-warning@+1{{capture 'self' was never used}}
522+
let v = sr3186 { f in { [unowned self, f] x in x != 1000 ? f(x + 1) : "success" } }(0)
523+
print("\(v)")
524+
}
525+
}

0 commit comments

Comments
 (0)